commit f6e611b35123e168a97c1e045d30b25a369b4b3a Author: xiaojin Date: Fri Jun 11 16:50:05 2021 +0800 hello world diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..15e3fae --- /dev/null +++ b/.dockerignore @@ -0,0 +1,42 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Log path +log/ +lib/ +tools/ +output/ + +# DB +db/ +dump/ + +# third party +gdb.txt +tags diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f9f032e --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +.idea +*.out +*.pid +*.o +*.so +*.lib +*.tar.gz +*.log +*.svn +.DS_Store* +._* +.DS_Store +*.dSYM +*.meta +.vscode \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e5fe818 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "framework/skynet"] + path = framework/skynet + url = https://github.com/cloudfreexiao/skynet.git diff --git a/.luacheckrc b/.luacheckrc new file mode 100644 index 0000000..82bc627 --- /dev/null +++ b/.luacheckrc @@ -0,0 +1,23 @@ +codes = true +color = true + +std = "max" + +include_files = { +} + +exclude_files = { +} + +globals = { + "SERVICE_NAME", + "class", + "handler", + "gLog", + "gEnv", +} + +ignore = { + "self", + "212/self", -- ignore self +} \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..12f8e3d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,4 @@ + - repo: https://github.com/cloudfreexiao/pre-commit-luacheck + rev: v1.0.0 + hooks: + - id: luacheck \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..551b6c1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,44 @@ +FROM ubuntu:20.04 + +LABEL maintainer="996442717qqcom@gmail.com" + +ENV ZEUS /opt/zeus +ENV ZEUS_BUILD_DIR /tmp/zeus +ENV PATH ${ZEUS}/bin:${PATH} + +COPY . ${ZEUS_BUILD_DIR} + +WORKDIR ${ZEUS_BUILD_DIR} + +RUN echo "Begin package ZEUS ..." + +RUN mkdir -p ${ZEUS}/framework + +RUN apt-get update -y && \ + apt-get install -y make autoconf libtool && \ + apt-get install -y gcc g++ && \ + apt-get install -y libreadline-dev && \ + apt-get install -y libssl-dev && \ + apt-get install -y libfaketime && \ + apt-get install -y google-perftools && \ + apt-get install -y libgoogle-perftools-dev && \ + apt-get install -y rlwrap && \ + cd ${ZEUS_BUILD_DIR}/framework/ && \ + make PLAT=linux && \ + cp -r ${ZEUS_BUILD_DIR}/framework/ ${ZEUS}/framework && \ + rm -rf ${ZEUS}/framework/3rd && \ + rm -rf ${ZEUS}/framework/lualib && \ + rm -rf ${ZEUS_BUILD_DIR} && \ + apt-get remove -y --purge make autoconf libtool && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +WORKDIR ${ZEUS} + +CMD ["sh", "-c", "${ZEUS}/framework/skynet/skynet ${ZEUS}/framework/skynet/example/config"] + +# 打包 +# docker build . -t cloudfreexiao/zeus:latest + +# 删除所有 容器和镜像 +# docker stop $(docker ps -a -q) && docker system prune --all --force \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..e69de29 diff --git a/docker/apollo/.env.dev b/docker/apollo/.env.dev new file mode 100755 index 0000000..ee4cd57 --- /dev/null +++ b/docker/apollo/.env.dev @@ -0,0 +1,17 @@ +# for compose only +COMPOSE_PROJECT_NAME=apollo-all-in-one +COMPOSE_FILE=apollo-docker-compose.yml +# common for apollo +SPRING_DATASOURCE_USERNAME=root +SPRING_DATASOURCE_PASSWORD=1234567 +EUREKA_INSTANCE_IP_ADDRESS=192.168.1.249 +# for apollo portal only +DEV_META=http://192.168.1.249:8073 +PROD_META=http://192.168.1.249:8073 +# other user defines +IMAGE_TAG=1.7.1 +SPRING_DATASOURCE_CONFIG_URL=jdbc:mysql://192.168.1.249:3306/ApolloConfigDB?characterEncoding=utf8 +SPRING_DATASOURCE_PORTAL_URL=jdbc:mysql://192.168.1.249:3306/ApolloPortalDB?characterEncoding=utf8 +CONFIG_SERVER_PORT=8073 +ADMIN_SERVER_PORT=8072 +PORTAL_SERVER_PORT=8071 diff --git a/docker/apollo/.env.prod b/docker/apollo/.env.prod new file mode 100755 index 0000000..c274db2 --- /dev/null +++ b/docker/apollo/.env.prod @@ -0,0 +1,17 @@ +# for compose only +COMPOSE_PROJECT_NAME=apollo-all-in-one +COMPOSE_FILE=apollo-docker-compose.yml +# common for apollo +SPRING_DATASOURCE_USERNAME=root +SPRING_DATASOURCE_PASSWORD=123456 +EUREKA_INSTANCE_IP_ADDRESS=172.16.141.109 +# for apollo portal only +DEV_META=http://172.16.141.109:8083 +PROD_META=http://172.16.141.109:8083 +# other user defines +IMAGE_TAG=1.7.1 +SPRING_DATASOURCE_CONFIG_URL=jdbc:mysql://rm-xxx.mysql.rds.aliyuncs.com:3306/ApolloConfigDB?characterEncoding=utf8 +SPRING_DATASOURCE_PORTAL_URL=jdbc:mysql://rm-xxx.mysql.rds.aliyuncs.com:3306/ApolloPortalDB?characterEncoding=utf8 +CONFIG_SERVER_PORT=8083 +ADMIN_SERVER_PORT=8093 +PORTAL_SERVER_PORT=8071 diff --git a/docker/apollo/.gitignore b/docker/apollo/.gitignore new file mode 100755 index 0000000..17c41f3 --- /dev/null +++ b/docker/apollo/.gitignore @@ -0,0 +1,2 @@ +.git/ +.idea/ \ No newline at end of file diff --git a/docker/apollo/README.md b/docker/apollo/README.md new file mode 100755 index 0000000..73f8a10 --- /dev/null +++ b/docker/apollo/README.md @@ -0,0 +1,32 @@ +## apollo-all-in-one-for-docker +docker 一键部署 Apollo 分布式配置中心(简化部署流程和运维复杂性:直接使用官方开源镜像,在 docker-compose 的环境下运行 ./startup_{profile}.sh 即可) +注:以下演示以 docker-compose 版本 1.25.0 为准 + +#### 使用 Apollo 至少要开三个服务,在容器化部署的过程中也踩了一些坑,于是自己动手写了个自动化部署运维脚本,解决了一些问题: +1. 一键部署(提前准备好各个环境的配置,如线下线上); +2. 使用 docker-compose + wait_for_it.sh 实现根据服务依赖关系顺序启动; +3. 在容器默认 bridge 网络模式下,解决 eureka 注册中心的 ip 问题 + +#### 使用说明(以 V1.7.1 版本为例) +###### 第一步:初始化数据库 +* https://github.com/ctripcorp/apollo/blob/v1.7.1/scripts/sql/apolloconfigdb.sql +* https://github.com/ctripcorp/apollo/blob/v1.7.1/scripts/sql/apolloportaldb.sql +* 更新注册中心地址: +``` +update ApolloConfigDB.serverconfig set `value` = 'http://172.16.141.109:8761/eureka/,http://172.16.141.110:8761/eureka/,http://172.16.141.111:8761/eureka/' where `key` = 'eureka.service.url'; +``` + +###### 第二步:更改各环境的配置 .env.{profile} + +###### 第三步:对应于各环境的启动脚本 startup_{profile}.sh + +#### 关于注册中心 +###### 默认值(数据库表:ApolloConfigDB.serverconfig) +```http://localhost:8080/eureka/``` + +###### 使用内置的注册中心 +* 在 .env.{profile} 设置环境变量 APOLLO_EUREKA_SERVER_ENABLED=true +* 更改数据库配置的注册中心地址,如:http://{宿主机 ip}:{configserver 端口}/eureka/ + +###### 使用外部的注册中心(默认支持) +* 直接更改数据库配置为外部的注册中心地址即可(当前服务所在的宿主机必须能够访问到的 ip 和端口) diff --git a/docker/apollo/apollo-docker-compose.yml b/docker/apollo/apollo-docker-compose.yml new file mode 100755 index 0000000..972df47 --- /dev/null +++ b/docker/apollo/apollo-docker-compose.yml @@ -0,0 +1,79 @@ +# usage of testing config: docker-compose --env-file .env.dev config +# usage of run: docker-compose --env-file .env.dev up -d +version: "3" +networks: + common-network: + driver: bridge +services: + apollo-configservice: + image: "apolloconfig/apollo-configservice:${IMAGE_TAG:-1.7.1}" + container_name: apollo-configservice + restart: always + hostname: apollo-configservice + networks: + common-network: + aliases: + - configservice + expose: + - ${CONFIG_SERVER_PORT} + ports: + - ${CONFIG_SERVER_PORT}:${CONFIG_SERVER_PORT} + volumes: + - "/tmp/apollologs:/opt/logs" + environment: + - SPRING_DATASOURCE_URL=${SPRING_DATASOURCE_CONFIG_URL} + - SPRING_DATASOURCE_USERNAME + - SPRING_DATASOURCE_PASSWORD + - EUREKA_INSTANCE_IP_ADDRESS + - APOLLO_EUREKA_SERVER_ENABLED=${APOLLO_EUREKA_SERVER_ENABLED:-false} + - SERVER_PORT=${CONFIG_SERVER_PORT} + apollo-adminservice: + image: "apolloconfig/apollo-adminservice:${IMAGE_TAG:-1.7.1}" + container_name: apollo-adminservice + restart: always + hostname: apollo-adminservice + networks: + common-network: + aliases: + - adminservice + expose: + - ${ADMIN_SERVER_PORT} + ports: + - ${ADMIN_SERVER_PORT}:${ADMIN_SERVER_PORT} + volumes: + - "/tmp/apollologs:/opt/logs" + environment: + - SPRING_DATASOURCE_URL=${SPRING_DATASOURCE_CONFIG_URL} + - SPRING_DATASOURCE_USERNAME + - SPRING_DATASOURCE_PASSWORD + - EUREKA_INSTANCE_IP_ADDRESS + - SERVER_PORT=${ADMIN_SERVER_PORT} + apollo-portal: + image: "apolloconfig/apollo-portal:${IMAGE_TAG:-1.7.1}" + container_name: apollo-portal + restart: always + hostname: apollo-portal + networks: + common-network: + aliases: + - portal + depends_on: + - apollo-configservice + - apollo-adminservice + expose: + - ${PORTAL_SERVER_PORT} + ports: + - ${PORTAL_SERVER_PORT}:${PORTAL_SERVER_PORT} + volumes: + - "/tmp/apollologs:/opt/logs" + environment: + - SPRING_DATASOURCE_URL=${SPRING_DATASOURCE_PORTAL_URL} + - SPRING_DATASOURCE_USERNAME + - SPRING_DATASOURCE_PASSWORD + - EUREKA_INSTANCE_IP_ADDRESS + - SERVER_PORT=${PORTAL_SERVER_PORT} + - APOLLO_PORTAL_ENVS=dev,prod + - DEV_META + - PROD_META + command: ["/bin/sh", "-c", "[[ ! -f wait-for-it.sh ]] && wget https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh && chmod +x wait-for-it.sh; ./wait-for-it.sh apollo-configservice:${CONFIG_SERVER_PORT} --timeout=300 --strict -- ./wait-for-it.sh apollo-adminservice:${ADMIN_SERVER_PORT} --timeout=300 --strict -- /apollo-portal/scripts/startup.sh"] + diff --git a/docker/apollo/startup_dev.sh b/docker/apollo/startup_dev.sh new file mode 100755 index 0000000..c501d48 --- /dev/null +++ b/docker/apollo/startup_dev.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker-compose --env-file .env.dev up -d diff --git a/docker/apollo/startup_prod.sh b/docker/apollo/startup_prod.sh new file mode 100755 index 0000000..f3cf5c0 --- /dev/null +++ b/docker/apollo/startup_prod.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker-compose --env-file .env.prod up -d diff --git a/docker/dns-server/README.md b/docker/dns-server/README.md new file mode 100644 index 0000000..e0ab7a6 --- /dev/null +++ b/docker/dns-server/README.md @@ -0,0 +1,9 @@ +# dns 服务 + +用于开发中区分测试环境、预生产、显示环境的域名解析 + +镜像使用 [https://hub.docker.com/r/andyshinn/dnsmasq](https://hub.docker.com/r/andyshinn/dnsmasq) + +## 优点 +1. 可以省去频繁的修改本地hosts文件 +2. 可统一公司内部人员的域名解析一致 diff --git a/docker/dns-server/docker-compose.yml b/docker/dns-server/docker-compose.yml new file mode 100644 index 0000000..f5c351b --- /dev/null +++ b/docker/dns-server/docker-compose.yml @@ -0,0 +1,14 @@ +version: '3' + +services: + dnsmasq: + image: andyshinn/dnsmasq:2.75 + cap_add: + - NET_ADMIN + ports: + - "53:53/tcp" + - "53:53/udp" + volumes: + - ./etc/resolv.dnsmasq:/etc/resolv.dnsmasq + - ./etc/dnsmasq.conf:/etc/dnsmasq.conf + - ./etc/dnsmasqhosts:/etc/dnsmasqhosts diff --git a/docker/dns-server/etc/dnsmasq.conf b/docker/dns-server/etc/dnsmasq.conf new file mode 100644 index 0000000..eb95baa --- /dev/null +++ b/docker/dns-server/etc/dnsmasq.conf @@ -0,0 +1,2 @@ +resolv-file=/etc/resolv.dnsmasq +addn-hosts=/etc/dnsmasqhosts diff --git a/docker/dns-server/etc/dnsmasqhosts b/docker/dns-server/etc/dnsmasqhosts new file mode 100644 index 0000000..603591a --- /dev/null +++ b/docker/dns-server/etc/dnsmasqhosts @@ -0,0 +1 @@ +192.168.9.9 hehe.my.com diff --git a/docker/dns-server/etc/resolv.dnsmasq b/docker/dns-server/etc/resolv.dnsmasq new file mode 100644 index 0000000..832d22e --- /dev/null +++ b/docker/dns-server/etc/resolv.dnsmasq @@ -0,0 +1,3 @@ +nameserver 114.114.114.114 +nameserver 223.6.6.6 +nameserver 8.8.8.8 diff --git a/docker/jaeger/README.md b/docker/jaeger/README.md new file mode 100644 index 0000000..ccfa380 --- /dev/null +++ b/docker/jaeger/README.md @@ -0,0 +1,3 @@ +# jaegertracing + +测试使用 如果生产使用,请使用官方 [jaeger-docker-compose.yml](https://github.com/jaegertracing/jaeger/blob/master/docker-compose/jaeger-docker-compose.yml) \ No newline at end of file diff --git a/docker/jaeger/docker-compose.yml b/docker/jaeger/docker-compose.yml new file mode 100644 index 0000000..89737b0 --- /dev/null +++ b/docker/jaeger/docker-compose.yml @@ -0,0 +1,15 @@ +version: '3' + +services: + jaegertracing: + image: jaegertracing/all-in-one:latest + ports: + - "5775:5775/udp" + - "6831:6831/udp" + - "6832:6832/udp" + - 5778:5778 + - 16686:16686 + - 14268:14268 + - 9411:9411 + environment: + COLLECTOR_ZIPKIN_HTTP_PORT: 9411 diff --git a/docker/prometheus/.gitignore b/docker/prometheus/.gitignore new file mode 100644 index 0000000..a6d8eaf --- /dev/null +++ b/docker/prometheus/.gitignore @@ -0,0 +1,3 @@ +grafana +prometheus +alertmanager \ No newline at end of file diff --git a/docker/prometheus/README.md b/docker/prometheus/README.md new file mode 100644 index 0000000..16f43fd --- /dev/null +++ b/docker/prometheus/README.md @@ -0,0 +1,6 @@ +# prometheus + +普罗米修斯监控系统 + +本配置实现 Prometheus+Alertmanager+Grafana + diff --git a/docker/prometheus/docker-compose.yml b/docker/prometheus/docker-compose.yml new file mode 100644 index 0000000..7043f88 --- /dev/null +++ b/docker/prometheus/docker-compose.yml @@ -0,0 +1,51 @@ +version: '3' + +services: + prometheus: + image: prom/prometheus:v2.15.1 + container_name: "prometheus" + logging: + options: + max-size: "1g" + networks: + - "prometheusnet" + volumes: + - ./prometheus/work:/prometheus + - ./prometheus/etc:/etc/prometheus + ports: + - 9999:9090 + command: + - --web.enable-lifecycle + - --config.file=/etc/prometheus/prometheus.yml + alertmanager: + image: prom/alertmanager:v0.20.0 + container_name: "alertmanager" + logging: + options: + max-size: "1g" + networks: + - "prometheusnet" + volumes: + - ./alertmanager/work:/alertmanager + - ./alertmanager/etc:/etc/alertmanager + ports: + - 9093:9093 + grafana: + image: grafana/grafana:6.1.4 + container_name: "grafana" + logging: + options: + max-size: "1g" + networks: + - "prometheusnet" + volumes: + - ./grafana/lib:/var/lib/grafana + - ./grafana/logs:/var/log/grafana + environment: + GF_SECURITY_ADMIN_PASSWORD: 123456 + ports: + - 3000:3000 + +networks: + prometheusnet: + driver: bridge \ No newline at end of file diff --git a/docker/redis/.gitignore b/docker/redis/.gitignore new file mode 100644 index 0000000..1269488 --- /dev/null +++ b/docker/redis/.gitignore @@ -0,0 +1 @@ +data diff --git a/docker/redis/README.md b/docker/redis/README.md new file mode 100644 index 0000000..214cadd --- /dev/null +++ b/docker/redis/README.md @@ -0,0 +1,3 @@ +# redis + +redis 3.x版本,如果自己需要其它版本请参考[https://hub.docker.com/r/library/redis/tags/](https://hub.docker.com/r/library/redis/tags/),修改docker-compose.yml中的版本`image: redis:x.x.x` diff --git a/docker/redis/docker-compose.yml b/docker/redis/docker-compose.yml new file mode 100644 index 0000000..68d0276 --- /dev/null +++ b/docker/redis/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3' + +services: + redis: + image: redis:3 + container_name: "redis3" + restart: always + ports: + - 6379:6379 + volumes: + - ./data:/data + networks: + - "redisnet" + # command: redis-server --appendonly yes + +networks: + redisnet: + driver: bridge \ No newline at end of file diff --git a/docker/tidb/.gitignore b/docker/tidb/.gitignore new file mode 100755 index 0000000..64536df --- /dev/null +++ b/docker/tidb/.gitignore @@ -0,0 +1,14 @@ +/values.yaml +/generated-docker-compose.yml +/data +/logs +/pd/bin +/tikv/bin +/tidb/bin +/tidb-vision/tidb-vision +/tmp +/docker/dashboard_installer/dashboard/overview.json +/docker/dashboard_installer/dashboard/pd.json +/docker/dashboard_installer/dashboard/tidb.json +/docker/dashboard_installer/dashboard/tikv.json +/.idea diff --git a/docker/tidb/.travis.yml b/docker/tidb/.travis.yml new file mode 100755 index 0000000..401c4a1 --- /dev/null +++ b/docker/tidb/.travis.yml @@ -0,0 +1,24 @@ +sudo: required + +services: + - docker + +before_install: + - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - + - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" + - sudo apt-get update + - sudo apt-get -y install docker-ce # update docker version + - sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose + - docker -v + - docker-compose -v + - docker-compose up -d + - sleep 10 # wait all components get ready + - docker-compose ps + - docker images + - docker network ls + - docker-compose logs + +script: + - docker ps -a --format="{{.Names}} {{.Image}} {{.Status}}" | grep -v 'Up' | grep -v 'Exited (0)' | awk '{print} END {if (NR>0) {exit 1;}}' + - docker-compose -f docker-compose-test.yml run --rm tispark-tests bash /opt/tispark-tests/tests/loaddata.sh # add some data for tests + # - docker-compose -f docker-compose-test.yml run --rm tispark-tests /opt/spark/bin/spark-submit /opt/spark/tests/tests.py # run tispark tests diff --git a/docker/tidb/LICENSE b/docker/tidb/LICENSE new file mode 100755 index 0000000..b67d909 --- /dev/null +++ b/docker/tidb/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/docker/tidb/README.md b/docker/tidb/README.md new file mode 100755 index 0000000..d972c1c --- /dev/null +++ b/docker/tidb/README.md @@ -0,0 +1,277 @@ +# TiDB docker-compose + +[![Build Status](https://travis-ci.org/pingcap/tidb-docker-compose.svg?branch=master)](https://travis-ci.org/pingcap/tidb-docker-compose) + +**WARNING: This is for testing only, DO NOT USE IN PRODUCTION!** + +## Requirements + +* Docker >= 17.03 +* Docker Compose >= 1.6.0 + +> **Note:** [Legacy Docker Toolbox](https://docs.docker.com/toolbox/toolbox_install_mac/) users must migrate to [Docker for Mac](https://store.docker.com/editions/community/docker-ce-desktop-mac), since it is tested that tidb-docker-compose cannot be started on Docker Toolbox and Docker Machine. + +## Quick start + +```bash +$ git clone https://github.com/pingcap/tidb-docker-compose.git +$ cd tidb-docker-compose && docker-compose pull # Get the latest Docker images +$ docker-compose up -d +$ mysql -h 127.0.0.1 -P 4000 -u root +``` + +* Access monitor at http://localhost:3000 (login with admin/admin if you want to modify grafana) + +* Access [tidb-vision](https://github.com/pingcap/tidb-vision) at http://localhost:8010 + +* Access Spark Web UI at http://localhost:8080 + and access [TiSpark](https://github.com/pingcap/tispark) through spark://127.0.0.1:7077 + +## Docker Swarm + +You can also use Docker Swarm to deploy a TiDB Platform cluster, and then you can scale the service using `docker stack` commands. + +```bash +$ docker swarm init # if your docker daemon is not already part of a swarm +$ mkdir -p data logs +$ docker stack deploy tidb -c docker-swarm.yml +$ mysql -h 127.0.0.1 -P 4000 -u root +``` + +After deploying the stack, you can scale the number of TiDB Server instances in the cluster like this: + +```bash +$ docker service scale tidb_tidb=2 +``` + +Docker Swarm automatically load-balances across the containers that implement a scaled service, which you can see if you execute `select @@hostname` several times: + +```bash +$ mysql -h 127.0.0.1 -P 4000 -u root -te 'select @@hostname' ++--------------+ +| @@hostname | ++--------------+ +| 340092e0ec9e | ++--------------+ +$ mysql -h 127.0.0.1 -P 4000 -u root -te 'select @@hostname' ++--------------+ +| @@hostname | ++--------------+ +| e6f05ffe6274 | ++--------------+ +$ mysql -h 127.0.0.1 -P 4000 -u root -te 'select @@hostname' ++--------------+ +| @@hostname | ++--------------+ +| 340092e0ec9e | ++--------------+ +``` + +If you want to connect to specific backend instances, for example to test concurrency by ensuring that you are connecting to distinct instances of tidb-server, you can use the `docker service ps` command to assemble a hostname for each container: + +```bash +$ docker service ps --no-trunc --format '{{.Name}}.{{.ID}}' tidb_tidb +tidb_tidb.1.x3sc2sd66a88phsj103ohr6qq +tidb_tidb.2.lk53apndq394cega46at853zw +``` + +To be able to resolve those hostnames, it's easiest to run the MySQL client in a container that has access to the swarm network: + +```bash +$ docker run --rm --network=tidb_default arey/mysql-client -h tidb_tidb.1.x3sc2sd66a88phsj103ohr6qq -P 4000 -u root -t -e 'select @@version' ++-----------------------------------------+ +| @@version | ++-----------------------------------------+ +| 5.7.25-TiDB-v3.0.0-beta.1-40-g873d9514b | ++-----------------------------------------+ +``` + +To loop through all instances of TiDB Server, you can use a bash loop like this: + +```bash +for host in $(docker service ps --no-trunc --format '{{.Name}}.{{.ID}}' tidb_tidb) + do docker run --rm --network tidb_default arey/mysql-client \ + -h "$host" -P 4000 -u root -te "select @@hostname" +done +``` + +To stop all services and remove all containers in the TiDB stack, execute `docker stack rm tidb`. + +## Customize TiDB Cluster + +### Configuration + +* config/pd.toml is copied from [PD repo](https://github.com/pingcap/pd/tree/master/conf) +* config/tikv.toml is copied from [TiKV repo](https://github.com/pingcap/tikv/tree/master/etc) +* config/tidb.toml is copied from [TiDB repo](https://github.com/pingcap/tidb/tree/master/config) +* config/pump.toml is copied from [TiDB-Binlog repo](https://github.com/pingcap/tidb-binlog/tree/master/cmd/pump) +* config/drainer.toml is copied from [TiDB-Binlog repo](https://github.com/pingcap/tidb-binlog/tree/master/cmd/drainer) + +If you find these configuration files outdated or mismatch with TiDB version, you can copy these files from their upstream repos and change their metrics addr with `pushgateway:9091`. Also `max-open-files` are configured to `1024` in tikv.toml to simplify quick start on Linux, because setting up ulimit on Linux with docker is quite tedious. + +And config/\*-dashboard.json are copied from [TiDB-Ansible repo](https://github.com/pingcap/tidb-ansible/tree/master/scripts) + +You can customize TiDB cluster configuration by editing docker-compose.yml and the above config files if you know what you're doing. + +But edit these files manually is tedious and error-prone, a template engine is strongly recommended. See the following steps + +### Install Helm + +[Helm](https://helm.sh) is used as a template render engine + +``` +curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get | bash +``` + +Or if you use Mac, you can use homebrew to install Helm by `brew install kubernetes-helm` + +### Bring up TiDB cluster + +```bash +$ git clone https://github.com/pingcap/tidb-docker-compose.git +$ cd tidb-docker-compose +$ vi compose/values.yaml # custom cluster size, docker image, port mapping etc +$ helm template compose > generated-docker-compose.yaml +$ docker-compose -f generated-docker-compose.yaml pull # Get the latest Docker images +$ docker-compose -f generated-docker-compose.yaml up -d + +# If you want to Bring up TiDB cluster with Binlog support +$ vi compose/values.yaml # set tidb.enableBinlog to true +$ helm template compose > generated-docker-compose-binlog.yaml +$ docker-compose -f generated-docker-compose-binlog.yaml up -d # or you can use 'docker-compose-binlog.yml' file directly + +# Note: If the value of drainer.destDBType is "kafka" and +# you want to consume the kafka messages outside the docker containers, +# please update the kafka.advertisedHostName with your docker host IP in compose/values.yaml and +# regenerate the 'generated-docker-compose-binlog.yaml' file +``` + +You can build docker image yourself for development test. + +* Build from binary + + For pd, tikv, tidb, pump and drainer comment their `image` and `buildPath` fields out. And then copy their binary files to pd/bin/pd-server, tikv/bin/tikv-server, tidb/bin/tidb-server, tidb-binlog/bin/pump and tidb-binlog/bin/drainer. + + These binary files can be built locally or downloaded from https://download.pingcap.org/tidb-latest-linux-amd64.tar.gz + + For tidbVision, comment its `image` and `buildPath` fields out. And then copy tidb-vision repo to tidb-vision/tidb-vision. + +* Build from source + + Leave pd, tikv, tidb and tidbVision `image` field empty and set their `buildPath` field to their source directory. + + For example, if your local tikv source directory is $GOPATH/src/github.com/pingcap/tikv, just set tikv `buildPath` to `$GOPATH/src/github.com/pingcap/tikv` + + *Note:* Compiling tikv from source consumes lots of memory, memory of Docker for Mac needs to be adjusted to greater than 6GB + +[tidb-vision](https://github.com/pingcap/tidb-vision) is a visiualization page of TiDB Cluster, it's WIP project and can be disabled by commenting `tidbVision` out. + +[TiSpark](https://github.com/pingcap/tispark) is a thin layer built for running Apache Spark on top of TiDB/TiKV to answer the complex OLAP queries. + +#### Host network mode (Linux) + +*Note:* Docker for Mac uses a Linux virtual machine, host network mode will not expose any services to host machine. So it's useless to use this mode. + +When using TiKV directly without TiDB, host network mode must be enabled. This way all services use host network without isolation. So you can access all services on the host machine. + +You can enable this mode by setting `networkMode: host` in compose/values.yaml and regenerate docker-compose.yml. When in this mode, prometheus address in configuration files should be changed from `prometheus:9090` to `127.0.0.1:9090`, and pushgateway address should be changed from `pushgateway:9091` to `127.0.0.1:9091`. + +These modification can be done by: +```bash +# Note: this only needed when networkMode is `host` +sed -i 's/pushgateway:9091/127.0.0.1:9091/g' config/* +sed -i 's/prometheus:9090/127.0.0.1:9090/g' config/* +``` + +After all the above is done, you can start tidb-cluster as usual by `docker-compose -f generated-docker-compose.yml up -d` + +### Debug TiDB/TiKV/PD instances +Prerequisites: + +Pprof: This is a tool for visualization and analysis of profiling data. Follow [these instructions](https://github.com/google/pprof#building-pprof) to install pprof. + +Graphviz: [http://www.graphviz.org/](http://www.graphviz.org/), used to generate graphic visualizations of profiles. + +* debug TiDB or PD instances + +```bash +### Use the following command to starts a web server for graphic visualizations of golang program profiles +$ ./tool/container_debug -s pd0 -p /pd-server -w +``` +The above command will produce graphic visualizations of profiles of `pd0` that can be accessed through the browser. + +* debug TiKV instances + +```bash +### step 1: select a tikv instance(here is tikv0) and specify the binary path in container to enter debug container +$ ./tool/container_debug -s tikv0 -p /tikv-server + +### after step 1, we can generate flame graph for tikv0 in debug container +$ ./run_flamegraph.sh 1 # 1 is the tikv0's process id + +### also can fetch tikv0's stack informations with GDB in debug container +$ gdb /tikv-server 1 -batch -ex "thread apply all bt" -ex "info threads" +``` + +### Access TiDB cluster + +TiDB uses ports: 4000(mysql) and 10080(status) by default + +```bash +$ mysql -h 127.0.0.1 -P 4000 -u root +``` + +And Grafana uses port 3000 by default, so open your browser at http://localhost:3000 to view monitor dashboard + +If you enabled tidb-vision, you can view it at http://localhost:8010 + +### Access Spark shell and load TiSpark + +Insert some sample data to the TiDB cluster: + +```bash +$ docker-compose exec tispark-master bash +$ cd /opt/spark/data/tispark-sample-data +$ mysql --local-infile=1 -h tidb -P 4000 -u root < dss.ddl +``` + +After the sample data is loaded into the TiDB cluster, you can access Spark Shell by `docker-compose exec tispark-master /opt/spark/bin/spark-shell`. + +```bash +$ docker-compose exec tispark-master /opt/spark/bin/spark-shell +... +Spark context available as 'sc' (master = local[*], app id = local-1527045927617). +Spark session available as 'spark'. +Welcome to + ____ __ + / __/__ ___ _____/ /__ + _\ \/ _ \/ _ `/ __/ '_/ + /___/ .__/\_,_/_/ /_/\_\ version 2.1.1 + /_/ + +Using Scala version 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_172) +Type in expressions to have them evaluated. +Type :help for more information. + +scala> import org.apache.spark.sql.TiContext +... +scala> val ti = new TiContext(spark) +... +scala> ti.tidbMapDatabase("TPCH_001") +... +scala> spark.sql("select count(*) from lineitem").show ++--------+ +|count(1)| ++--------+ +| 60175| ++--------+ +``` + +You can also access Spark with Python or R using the following commands: + +``` +docker-compose exec tispark-master /opt/spark/bin/pyspark +docker-compose exec tispark-master /opt/spark/bin/sparkR +``` + +More documents about TiSpark can be found [here](https://github.com/pingcap/tispark). diff --git a/docker/tidb/compose/Chart.yaml b/docker/tidb/compose/Chart.yaml new file mode 100755 index 0000000..53e4cfc --- /dev/null +++ b/docker/tidb/compose/Chart.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +description: tidb-docker-compose +name: tidb-docker-compose +version: 0.1.0 +home: https://github.com/pingcap/tidb-docker-compose +sources: + - https://github.com/pingcap/tidb-docker-compose +keywords: + - newsql + - htap + - database + - mysql + - raft diff --git a/docker/tidb/compose/templates/_helpers.tpl b/docker/tidb/compose/templates/_helpers.tpl new file mode 100755 index 0000000..f98f22c --- /dev/null +++ b/docker/tidb/compose/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{- define "initial_cluster" }} + {{- range until (.Values.pd.size | int) }} + {{- if . -}} + , + {{- end -}} + pd{{ . }}=http:// + {{- if eq $.Values.networkMode "host" -}} + 127.0.0.1:{{add (add ($.Values.pd.port | int) 10000) . }} + {{- else -}} + pd{{ . }}:2380 + {{- end -}} + {{- end -}} +{{- end -}} + +{{- define "pd_list" }} + {{- range until (.Values.pd.size | int) }} + {{- if . -}} + , + {{- end -}} + {{- if eq $.Values.networkMode "host" -}} + 127.0.0.1:{{ add ($.Values.pd.port | int) . }} + {{- else -}} + pd{{ . }}:2379 + {{- end -}} + {{- end -}} +{{- end -}} + +{{- define "pd_urls" }} + {{- range until (.Values.pd.size | int) }} + {{- if . -}} + , + {{- end -}} + {{- if eq $.Values.networkMode "host" -}} + http://127.0.0.1:{{ add ($.Values.pd.port | int) . }} + {{- else -}} + http://pd{{ . }}:2379 + {{- end -}} + {{- end -}} +{{- end -}} + +{{- define "zoo_servers" }} + {{- range until (.Values.zookeeper.size | int) }} + {{- if eq $.Values.networkMode "host" -}} + {{- if . }} {{ end }}server.{{ add . 1 }}=127.0.0.1:{{ add . 2888 }}:{{ add . 3888 }} + {{- else -}} + {{- if . }} {{ end }}server.{{ add . 1 }}=zoo{{ . }}:2888:3888 + {{- end -}} + {{- end -}} +{{- end -}} + +{{- define "zoo_connect" }} + {{- range until (.Values.zookeeper.size | int) }} + {{- if . -}} + , + {{- end -}} + {{- if eq $.Values.networkMode "host" -}} + 127.0.0.1:{{ add $.Values.zookeeper.port . }} + {{- else -}} + zoo{{ add . }}:{{ add $.Values.zookeeper.port . }} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/docker/tidb/compose/templates/docker-compose.yml b/docker/tidb/compose/templates/docker-compose.yml new file mode 100755 index 0000000..9ca7221 --- /dev/null +++ b/docker/tidb/compose/templates/docker-compose.yml @@ -0,0 +1,431 @@ +{{- $pdSize := .Values.pd.size | int }} +{{- $tikvSize := .Values.tikv.size | int }} +{{- $pdPort := .Values.pd.port | int }} +{{- $pdPeerPort := add $pdPort 10000 }} +{{- $tikvPort := .Values.tikv.port | int -}} +{{- $pumpSize := .Values.pump.size | int }} +{{- $pumpPort := .Values.pump.port | int }} +{{- $zooSize := .Values.zookeeper.size | int }} +{{- $zooPort := .Values.zookeeper.port | int }} +{{- $kafkaSize := .Values.kafka.size | int }} +{{- $kafkaPort := .Values.kafka.port | int }} +version: '2.1' + +services: + {{- range until $pdSize }} + pd{{ . }}: + {{- if $.Values.pd.image }} + image: {{ $.Values.pd.image }} + {{- else }} + image: pd:latest + build: + context: {{ $.Values.pd.buildPath | default "./pd" }} + dockerfile: {{ $.Values.pd.dockerfile | default "Dockerfile" }} + {{- end }} + {{- if eq $.Values.networkMode "host" }} + network_mode: host + {{- else }} + ports: + - "2379" + {{- end }} + volumes: + - ./config/pd.toml:/pd.toml:ro + - {{ $.Values.dataDir }}:/data + - {{ $.Values.logsDir }}:/logs + command: + - --name=pd{{ . }} + {{- if eq $.Values.networkMode "host" }} + - --client-urls=http://0.0.0.0:{{ add $pdPort . }} + - --peer-urls=http://0.0.0.0:{{ add $pdPeerPort . }} + - --advertise-client-urls=http://127.0.0.1:{{ add $pdPort . }} + - --advertise-peer-urls=http://127.0.0.1:{{ add $pdPeerPort . }} + {{- else }} + - --client-urls=http://0.0.0.0:2379 + - --peer-urls=http://0.0.0.0:2380 + - --advertise-client-urls=http://pd{{ . }}:2379 + - --advertise-peer-urls=http://pd{{ . }}:2380 + {{- end }} + - --initial-cluster={{- template "initial_cluster" $ }} + - --data-dir=/data/pd{{ . }} + - --config=/pd.toml + - --log-file=/logs/pd{{ . }}.log + # sysctls: + # net.core.somaxconn: 32768 + # ulimits: + # nofile: + # soft: 1000000 + # hard: 1000000 + restart: on-failure + {{ end }} + + {{- range until $tikvSize }} + tikv{{ . }}: + {{- if $.Values.tikv.image }} + image: {{ $.Values.tikv.image }} + {{- else }} + image: tikv:latest + build: + context: {{ $.Values.tikv.buildPath | default "./tikv" }} + dockerfile: {{ $.Values.tikv.dockerfile | default "Dockerfile" }} + {{- end }} + {{- if eq $.Values.networkMode "host" }} + network_mode: host + {{- end }} + volumes: + - ./config/tikv.toml:/tikv.toml:ro + - {{ $.Values.dataDir }}:/data + - {{ $.Values.logsDir }}:/logs + command: + {{- if eq $.Values.networkMode "host" }} + - --addr=0.0.0.0:{{ add $tikvPort . }} + - --advertise-addr=127.0.0.1:{{ add $tikvPort . }} + {{- else }} + - --addr=0.0.0.0:20160 + - --advertise-addr=tikv{{ . }}:20160 + {{- end }} + - --data-dir=/data/tikv{{ . }} + - --pd={{- template "pd_list" $ }} + - --config=/tikv.toml + - --log-file=/logs/tikv{{ . }}.log + depends_on: + {{- range until $pdSize }} + - "pd{{.}}" + {{- end }} + # sysctls: + # net.core.somaxconn: 32768 + # ulimits: + # nofile: + # soft: 1000000 + # hard: 1000000 + restart: on-failure + {{ end }} + + {{- if .Values.tidb }} + {{- if .Values.tidb.enableBinlog }} + {{- range until $pumpSize }} + pump{{ . }}: + {{- if $.Values.pump.image }} + image: {{ $.Values.pump.image }} + {{- else }} + image: tidb-binlog:latest + build: + context: {{ $.Values.pump.buildPath | default "./tidb-binlog" }} + dockerfile: {{ $.Values.pump.dockerfile | default "Dockerfile" }} + {{- end }} + {{- if eq $.Values.networkMode "host" }} + network_mode: host + {{- end }} + volumes: + - ./config/pump.toml:/pump.toml:ro + - {{ $.Values.dataDir }}:/data + - {{ $.Values.logsDir }}:/logs + command: + - /pump + {{- if eq $.Values.networkMode "host" }} + - --addr=0.0.0.0:{{ add $pumpPort . }} + - --advertise-addr=127.0.0.1:{{ add $pumpPort . }} + {{- else }} + - --addr=0.0.0.0:8250 + - --advertise-addr=pump{{ . }}:8250 + {{- end }} + - --data-dir=/data/pump{{ . }} + - --log-file=/logs/pump{{ . }}.log + - --node-id=pump{{ . }} + - --pd-urls={{- template "pd_urls" $ }} + - --config=/pump.toml + depends_on: + {{- range until $pdSize }} + - "pd{{.}}" + {{- end }} + restart: on-failure + {{ end }} + drainer: + {{- if $.Values.drainer.image }} + image: {{ $.Values.drainer.image }} + {{- else }} + image: tidb-binlog:latest + build: + context: {{ $.Values.drainer.buildPath | default "./tidb-binlog" }} + dockerfile: {{ $.Values.drainer.dockerfile | default "Dockerfile" }} + {{- end }} + {{- if eq $.Values.networkMode "host" }} + network_mode: host + {{- end }} + volumes: + - ./config/drainer.toml:/drainer.toml:ro + - {{ $.Values.dataDir }}:/data + - {{ $.Values.logsDir }}:/logs + command: + - /drainer + - --addr=0.0.0.0:8249 + - --data-dir=/data/data.drainer + - --log-file=/logs/drainer.log + - --pd-urls={{- template "pd_urls" $ }} + - --config=/drainer.toml + - --initial-commit-ts=0 + {{- if eq $.Values.drainer.destDBType "kafka" }} + - --dest-db-type=kafka + {{- end }} + depends_on: + {{- range until $pdSize }} + - "pd{{ . }}" + {{- end }} + {{- if eq $.Values.drainer.destDBType "kafka" }} + {{- range until $kafkaSize }} + - "kafka{{ . }}" + {{- end }} + {{- end }} + restart: on-failure + + {{- if eq $.Values.drainer.destDBType "kafka" }} + {{ range until $zooSize }} + zoo{{ . }}: + image: zookeeper:latest + {{- if eq $.Values.networkMode "host" }} + network_mode: host + {{- else }} + ports: + - "{{ add $zooPort . }}:{{ add $zooPort . }}" + {{- end }} + environment: + ZOO_MY_ID: {{ add . 1 }} + ZOO_PORT: {{ add $zooPort . }} + ZOO_SERVERS: {{ template "zoo_servers" $ }} + volumes: + - {{ $.Values.dataDir }}/zoo{{ . }}/data:/data + - {{ $.Values.dataDir }}/zoo{{ . }}/datalog:/datalog + restart: on-failure + {{ end }} + + {{- range until $kafkaSize }} + kafka{{ . }}: + image: {{ $.Values.kafka.image }} + {{- if eq $.Values.networkMode "host" }} + network_mode: host + {{- else }} + ports: + - "{{ add . $kafkaPort }}:{{ add . $kafkaPort }}" + {{- end }} + environment: + KAFKA_BROKER_ID: {{ add . 1 }} + KAFKA_LOG_DIRS: /data/kafka-logs + {{- if $.Values.kafka.advertisedHostName }} + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://{{ $.Values.kafka.advertisedHostName }}:{{ add . $kafkaPort }} + {{- else }} + {{- if eq $.Values.networkMode "host" }} + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://127.0.0.1:{{ add . $kafkaPort }} + {{- else }} + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka{{ . }}:{{ add . $kafkaPort }} + {{- end }} + {{- end }} + KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:{{ add . $kafkaPort }} + KAFKA_ZOOKEEPER_CONNECT: {{ template "zoo_connect" $ }} + volumes: + - {{ $.Values.dataDir }}/kafka-logs/kafka{{ . }}:/data/kafka-logs + - {{ $.Values.logsDir }}/kafka{{ . }}:/opt/kafka/logs + - /var/run/docker.sock:/var/run/docker.sock + depends_on: + {{- range until $zooSize }} + - "zoo{{ . }}" + {{- end }} + restart: on-failure + {{- end }} + {{- end }} + {{- end }} + {{ end }} + + {{- if .Values.tidb }} + tidb: + {{- if .Values.tidb.image }} + image: {{ .Values.tidb.image }} + {{- else }} + image: tidb:latest + build: + context: {{ .Values.tidb.buildPath | default "./tidb" }} + dockerfile: {{ .Values.tidb.dockerfile | default "Dockerfile" }} + {{- end }} + {{- if eq .Values.networkMode "host" }} + network_mode: host + {{- else }} + ports: + - "{{ .Values.tidb.mysqlPort }}:4000" + - "{{ .Values.tidb.statusPort }}:10080" + {{- end }} + volumes: + - ./config/tidb.toml:/tidb.toml:ro + - {{ .Values.logsDir }}:/logs + command: + - --store=tikv + - --path={{- template "pd_list" $ }} + - --config=/tidb.toml + - --log-file=/logs/tidb.log + - --advertise-address=tidb + {{- if .Values.tidb.enableBinlog }} + - --enable-binlog=true + {{- end }} + depends_on: + {{- range until $tikvSize }} + - "tikv{{.}}" + {{- end }} + {{- if .Values.tidb.enableBinlog }} + {{- range until $pumpSize }} + - "pump{{.}}" + {{- end }} + {{- end }} + # sysctls: + # net.core.somaxconn: 32768 + # ulimits: + # nofile: + # soft: 1000000 + # hard: 1000000 + restart: on-failure + {{ end }} + + {{- if .Values.tispark }} + tispark-master: + {{- if .Values.tispark.image }} + image: {{ .Values.tispark.image }} + {{- else }} + image: tispark:latest + build: + context: {{ .Values.tispark.buildPath | default "./tispark" }} + dockerfile: {{ .Values.tispark.dockerfile | default "Dockerfile" }} + {{- end }} + command: + - /opt/spark/sbin/start-master.sh + volumes: + - ./config/spark-defaults.conf:/opt/spark/conf/spark-defaults.conf:ro + environment: + SPARK_MASTER_PORT: {{ .Values.tispark.masterPort }} + SPARK_MASTER_WEBUI_PORT: {{ .Values.tispark.webuiPort }} + ports: + - "{{ .Values.tispark.masterPort }}:7077" + - "{{ .Values.tispark.webuiPort }}:8080" + depends_on: + {{- range until $tikvSize }} + - "tikv{{.}}" + {{- end }} + restart: on-failure + {{- range until ( .Values.tispark.workerCount | int ) }} + tispark-slave{{ . }}: + {{- if $.Values.tispark.image }} + image: {{ $.Values.tispark.image }} + {{- else }} + image: tispark:latest + build: + context: {{ $.Values.tispark.buildPath | default "./tispark" }} + dockerfile: {{ $.Values.tispark.dockerfile | default "Dockerfile" }} + {{- end }} + command: + - /opt/spark/sbin/start-slave.sh + - spark://tispark-master:7077 + volumes: + - ./config/spark-defaults.conf:/opt/spark/conf/spark-defaults.conf:ro + environment: + SPARK_WORKER_WEBUI_PORT: {{ add $.Values.tispark.workerWebUIPort . }} + ports: + - "{{ add $.Values.tispark.workerWebUIPort . }}:{{ add $.Values.tispark.workerWebUIPort . }}" + depends_on: + - tispark-master + restart: on-failure + {{- end }} + {{ end }} + + {{- if .Values.tidbVision }} + tidb-vision: + {{- if .Values.tidbVision.image }} + image: {{ .Values.tidbVision.image }} + {{- else }} + image: tidb-vision:latest + build: + context: {{ .Values.tidbVision.buildPath | default "./tidb-vision" }} + dockerfile: {{ .Values.tidbVision.dockerfile | default "Dockerfile" }} + {{- end }} + environment: + PD_ENDPOINT: {{if eq .Values.networkMode "host"}}127.0.0.1:{{.Values.pd.port}}{{else}}pd0:2379{{end}} + {{- if eq .Values.networkMode "host" }} + PORT: {{ .Values.tidbVision.port }} + network_mode: host + {{- else }} + ports: + - "{{ .Values.tidbVision.port }}:8010" + {{- end }} + restart: on-failure + {{- end }} + + {{- if .Values.prometheus }} + pushgateway: + image: {{ .Values.pushgateway.image }} + {{- if eq .Values.networkMode "host" }} + command: + - --web.listen-address=0.0.0.0:{{.Values.pushgateway.port}} + - --log.level={{ .Values.pushgateway.logLevel }} + network_mode: host + {{- else }} + command: + - --log.level={{ .Values.pushgateway.logLevel }} + {{- end }} + restart: on-failure + + prometheus: + user: root + image: {{ .Values.prometheus.image }} + command: + - --log.level={{ .Values.prometheus.logLevel }} + - --storage.tsdb.path=/data/prometheus + - --config.file=/etc/prometheus/prometheus.yml + {{- if eq .Values.networkMode "host" }} + - --web.listen-address=0.0.0.0:{{.Values.prometheus.port}} + network_mode: host + {{- else }} + ports: + - "{{ .Values.prometheus.port }}:9090" + {{- end }} + volumes: + - ./config/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - ./config/pd.rules.yml:/etc/prometheus/pd.rules.yml:ro + - ./config/tikv.rules.yml:/etc/prometheus/tikv.rules.yml:ro + - ./config/tidb.rules.yml:/etc/prometheus/tidb.rules.yml:ro + - {{ .Values.dataDir }}:/data + restart: on-failure + {{- end }} + + {{- if .Values.grafana }} + grafana: + image: {{ .Values.grafana.image }} + user: "0" + {{- if eq .Values.networkMode "host" }} + network_mode: host + environment: + GF_SERVER_HTTP_PORT: {{ .Values.grafana.port }} + GF_LOG_LEVEL: {{ .Values.grafana.logLevel }} + {{- else }} + environment: + GF_LOG_LEVEL: {{ .Values.grafana.logLevel }} + GF_PATHS_PROVISIONING: /etc/grafana/provisioning + GF_PATHS_CONFIG: /etc/grafana/grafana.ini + ports: + - "{{ .Values.grafana.port }}:3000" + {{- end }} + volumes: + - ./config/grafana:/etc/grafana + - ./config/dashboards:/tmp/dashboards + - ./data/grafana:/var/lib/grafana + restart: on-failure + dashboard-installer: + {{- if .Values.dashboardInstaller.image }} + image: {{ .Values.dashboardInstaller.image }} + {{- else }} + image: tidb-dashboard-installer:latest + build: + context: {{ .Values.dashboardInstaller.buildPath | default "./dashboard-installer" }} + dockerfile: {{ .Values.dashboardInstaller.dockerfile | default "Dockerfile" }} + {{- end }} + {{- if eq .Values.networkMode "host" }} + network_mode: host + command: ["127.0.0.1:{{.Values.grafana.port}}"] + {{- else }} + command: ["grafana:3000"] + {{- end }} + restart: on-failure + {{- end -}} diff --git a/docker/tidb/compose/values.yaml b/docker/tidb/compose/values.yaml new file mode 100755 index 0000000..4b837ad --- /dev/null +++ b/docker/tidb/compose/values.yaml @@ -0,0 +1,125 @@ +dataDir: ./data +logsDir: ./logs +# supported networkMode: bridge | host +# host network mode is useless on Mac +networkMode: bridge + +pd: + size: 3 + image: pingcap/pd:latest + + # If you want to build pd image from source, leave image empty and specify pd source directory + # and its dockerfile name + # buildPath: ./pd + # dockerfile: Dockerfile + # when network_mode is host, pd port ranges [port, port+size) + port: 2379 + +tikv: + size: 3 + image: pingcap/tikv:latest + + # If you want to build tikv image from source, leave image empty and specify tikv source directory + # and its dockerfile name + # buildPath: ./tikv + # dockerfile: Dockerfile + # when network mode is host, tikv port ranges [port, port+size) + port: 20160 + +# comment this section out if you don't need SQL layer and want to use TiKV directly +# when using TiKV directly, networkMode must be set to `host` +tidb: + image: pingcap/tidb:latest + + # If you want to build tidb image from source, leave image empty and specify tidb source directory + # and its dockerfile name + # buildPath: ./tidb + # dockerfile: Dockerfile + mysqlPort: "4000" + statusPort: "10080" + enableBinlog: false + +pump: + size: 3 + image: pingcap/tidb-binlog:latest + + # If you want to build pump image from source, leave image empty and specify pump source directory + # and its dockerfile name + # buildPath: ./pump + # dockerfile: Dockerfile + # when network_mode is host, pump port ranges [port, port+size) + port: 8250 + +drainer: + image: pingcap/tidb-binlog:latest + + # If you want to build drainer image from source, leave image empty and specify drainer source directory + # and its dockerfile name + # buildPath: ./drainer + # dockerfile: Dockerfile + destDBType: "kafka" + +zookeeper: + size: 3 + image: zookeeper:latest + port: 2181 + +kafka: + size: 3 + image: wurstmeister/kafka:2.12-2.1.1 + # If you want to consume the kafka messages outside the docker containers, + # Please update the advertisedHostName with your docker host IP + advertisedHostName: + port: 9092 + +tispark: + image: pingcap/tispark:latest + + # If you want to build tidb image from source, leave image empty and specify tidb source directory + # and its dockerfile name + # buildPath: ./tidb + # dockerfile: Dockerfile + buildPath: ./tispark + dockerfile: Dockerfile + + masterPort: 7077 + webuiPort: 8080 + workerCount: 1 + # slave web ui port will be workerWebUIPort ~ workerWebUIPort+workerCount-1 + workerWebUIPort: 38081 + +# comment this out to disable tidb-vision +tidbVision: + image: pingcap/tidb-vision:latest + + # If you want to build tidb-vision image from source, leave image empty and specify tidb-vision source directory + # and its dockerfile name + # buildPath: ./tidb-vision + # dockerfile: Dockerfile + port: "8010" + +# comment following monitor components sections out to disable monitor +grafana: + image: grafana/grafana:5.3.0 + port: "3000" + logLevel: error + +pushgateway: + image: prom/pushgateway:v0.3.1 + port: "9091" + logLevel: error + +prometheus: + image: prom/prometheus:v2.2.1 + port: "9090" + logLevel: error + +# This is used to import tidb monitor dashboard templates to grafana +# this container runs only once and keep running until templates imported successfully +dashboardInstaller: + image: pingcap/tidb-dashboard-installer:v2.0.0 + + # If you want to build tidb-dashboard-installer image from source, leave image empty and specify tidb-dashboard-installer source directory + # and its dockerfile name + # buildPath: ./dashboard-installer + # dockerfile: Dockerfile diff --git a/docker/tidb/config/dashboards/README.md b/docker/tidb/config/dashboards/README.md new file mode 100755 index 0000000..42a9770 --- /dev/null +++ b/docker/tidb/config/dashboards/README.md @@ -0,0 +1,9 @@ +# TiDB dashboard +With Grafana v5.x or later, we can use provisioning feature to statically provision datasources and dashboards. No need to use scripts to configure Grafana. + +The JSON files in dashboards are copied from [tidb-ansible](https://github.com/pingcap/tidb-ansible/tree/master/scripts), and need to replace variables in the json file(It was did by python file before). + +It is used in [tidb-docker-compose](https://github.com/pingcap/tidb-docker-compose) and [tidb-operator](https://github.com/pingcap/tidb-operator). + + + diff --git a/docker/tidb/config/dashboards/overview.json b/docker/tidb/config/dashboards/overview.json new file mode 100755 index 0000000..d15c146 --- /dev/null +++ b/docker/tidb/config/dashboards/overview.json @@ -0,0 +1,3867 @@ +{ + "__requires": [ + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "singlestat", + "name": "Singlestat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "tidb-cluster", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": false, + "gnetId": null, + "graphTooltip": 0, + "hideControls": false, + "id": null, + "links": [], + "refresh": "1m", + "rows": [ + { + "collapse": false, + "height": 250, + "panels": [ + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "tidb-cluster", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 29, + "interval": null, + "links": [], + "mappingType": 2, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "1", + "text": "Leader", + "to": "100000" + }, + { + "from": "0", + "text": "Follower", + "to": "1" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "delta(pd_server_tso{type=\"save\",instance=\"$instance\"}[1m])", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "", + "metric": "pd_server_tso", + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "PD Role", + "type": "singlestat", + "valueFontSize": "50%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "1" + }, + { + "op": "=", + "text": "", + "value": "" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "tidb-cluster", + "decimals": null, + "editable": false, + "error": false, + "format": "decbytes", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": false + }, + "id": 27, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(77, 135, 25, 0.18)", + "full": true, + "lineColor": "rgb(21, 179, 65)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"storage_capacity\"}", + "intervalFactor": 2, + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "Storage Capacity", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "format": "decbytes", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "hideTimeOverride": false, + "id": 28, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"storage_size\"}", + "intervalFactor": 2, + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "Current Storage Size", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "tidb-cluster", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 30, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"region_count\"}", + "intervalFactor": 2, + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "Number of Regions", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "tidb-cluster", + "format": "percentunit", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 64, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 1, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "1 - min(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_score\"}) / max(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_score\"})", + "format": "time_series", + "interval": "15s", + "intervalFactor": 2, + "refId": "A" + } + ], + "thresholds": "0.01,0.5", + "title": "Leader Balance Ratio", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "tidb-cluster", + "format": "percentunit", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 65, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 1, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "1 - min(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_score\"}) / max(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_score\"})", + "format": "time_series", + "interval": "15s", + "intervalFactor": 2, + "refId": "A" + } + ], + "thresholds": "0.05,0.5", + "title": "Region Balance Ratio", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "columns": [ + { + "text": "Current", + "value": "current" + } + ], + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fontSize": "100%", + "hideTimeOverride": false, + "id": 18, + "links": [], + "pageSize": null, + "repeat": null, + "scroll": false, + "showHeader": true, + "sort": { + "col": null, + "desc": false + }, + "span": 2, + "styles": [ + { + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Metric", + "sanitize": false, + "type": "string" + }, + { + "colorMode": "cell", + "colors": [ + "rgba(50, 172, 45, 0.97)", + "rgba(237, 129, 40, 0.89)", + "rgba(245, 54, 54, 0.9)" + ], + "decimals": 0, + "pattern": "Current", + "thresholds": [ + "1", + "2" + ], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_up_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Up Stores", + "metric": "pd_cluster_status", + "refId": "A", + "step": 30 + }, + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_disconnected_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Disconnect Stores", + "metric": "pd_cluster_status", + "refId": "B", + "step": 30 + }, + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_low_space_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "LowSpace Stores", + "metric": "pd_cluster_status", + "refId": "C", + "step": 30 + }, + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_down_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Down Stores", + "metric": "pd_cluster_status", + "refId": "D", + "step": 30 + }, + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_offline_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Offline Stores", + "metric": "pd_cluster_status", + "refId": "E", + "step": 30 + }, + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_tombstone_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Tombstone Stores", + "metric": "pd_cluster_status", + "refId": "F", + "step": 30 + } + ], + "title": "Store Status", + "transform": "timeseries_aggregations", + "transparent": false, + "type": "table" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 24, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(grpc_server_handling_seconds_bucket{instance=\"$instance\"}[5m])) by (grpc_method, le))", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{grpc_method}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% completed_cmds_duration_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 32, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.98, sum(rate(pd_client_request_handle_requests_duration_seconds_bucket[30s])) by (type, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}} 98th percentile", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(rate(pd_client_request_handle_requests_duration_seconds_sum[30s])) by (type) / avg(rate(pd_client_request_handle_requests_duration_seconds_count[30s])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}} average", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "handle_requests_duration_seconds", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "s", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 66, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_regions_status{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + }, + { + "expr": "sum(pd_regions_status) by (instance, type)", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region Health", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 0, + "id": 68, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_write_region_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot write region's leader distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 0, + "fill": 0, + "id": 69, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_read_region_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot read region's leader distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 0, + "id": 33, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_region_heartbeat{instance=\"$instance\", type=\"report\", status=\"ok\"}[1m])) by (store)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region heartbeat report", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 0, + "id": 67, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(pd_scheduler_region_heartbeat_latency_seconds_bucket[5m])) by (store, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% region heartbeat latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": "instance", + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "PD", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 2, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_executor_statement_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Statement OPS", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 34, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "hideEmpty": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 3, + "legendFormat": "99", + "refId": "B", + "step": 15 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "95", + "refId": "C" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 0, + "id": 35, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(tidb_server_query_total[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} {{type}} {{result}}", + "refId": "A", + "step": 20 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "QPS By Instance", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 0, + "id": 72, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_server_execute_error_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": " {{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Failed Query OPM", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 4, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "fill": 0, + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": true, + "targets": [ + { + "expr": "tidb_server_connections", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(tidb_server_connections)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "total", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Connection Count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 0, + "id": 36, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "go_memstats_heap_inuse_bytes{job=~\"tidb.*\"}", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{job}}", + "metric": "go_memstats_heap_inuse_bytes", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Heap Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 70, + "legend": { + "avg": false, + "current": true, + "max": true, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_session_transaction_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Transaction OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 71, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "95", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Transaction Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 37, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_txn_cmd_duration_seconds_count[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Cmd OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 38, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_tikvclient_txn_cmd_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Cmd Duration 99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 39, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_client_cmd_handle_cmds_duration_seconds_count{type=\"tso\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "cmd", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(pd_client_request_handle_requests_duration_seconds_count{type=\"tso\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "request", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD TSO OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 40, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "hideEmpty": false, + "hideZero": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD TSO Wait Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 2, + "fill": 0, + "id": 41, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_region_err_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tidb_server_session_execute_parse_duration_count", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "TiClient Region Error OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 42, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_lock_resolver_actions_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tidb_tikvclient_lock_resolver_actions_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Lock Resolve OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": null, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 6, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": false, + "hideZero": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_domain_load_schema_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Load Schema Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 43, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": true, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_backoff_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Backoff OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "TiDB", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 299, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 20, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{type=\"leader\"}) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 10 + }, + { + "expr": "delta(tikv_pd_heartbeat_tick_total{type=\"leader\"}[30s]) < -10", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "total", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "leader", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 21, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{type=\"region\"}) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "region", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 0, + "id": 75, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "CPU", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 0, + "id": 74, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(process_resident_memory_bytes{instance=~\"$instance\"}) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memory", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 5, + "id": 44, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 0, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_size_bytes) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "store size", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 73, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_size_bytes) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "cf size", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 3, + "grid": {}, + "id": 17, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_channel_full_total[1m])) by (instance, type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - {{type}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "channel full", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 11, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_server_report_failure_msg_total[1m])) by (type,instance,store_id)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - {{type}} - to - {{store_id}}", + "metric": "tikv_server_raft_store_msg_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "server report failures", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 46, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_scheduler_contex_total) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler pending commands", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 45, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": false, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_pending_request[1m])) by (req, priority)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ req }} - {{priority}}", + "metric": "tikv_region_written_keys_bucket", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_coprocessor_pending_request[1m])) BY (type, instance)", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor pending requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 51, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_executor_count[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_coprocessor_request_error", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor executor count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 5, + "fill": 1, + "id": 47, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_coprocessor_request_duration_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-99%", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_duration_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-95%", + "refId": "B", + "step": 10 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_duration_seconds_sum{req=\"select\"}[1m])) / sum(rate(tikv_coprocessor_request_duration_seconds_count{req=\"select\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "select-avg", + "refId": "C", + "step": 10 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_duration_seconds_sum{req=\"index\"}[1m])) / sum(rate(tikv_coprocessor_request_duration_seconds_count{req=\"index\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "index-avg", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor request duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 0, + "id": 48, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"raftstore_.*\"}[1m])) by (instance, name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "raft store CPU", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 0, + "id": 49, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"cop_.*\"}[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_coprocessor_request_duration_seconds_bucket", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Coprocessor CPU", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "TiKV", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "tidb-cluster", + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "instance", + "options": [], + "query": "label_values(pd_cluster_status, instance)", + "refresh": 1, + "regex": "", + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": {}, + "datasource": "tidb-cluster", + "hide": 0, + "includeAll": true, + "label": "Namespace", + "multi": false, + "name": "namespace", + "options": [], + "query": "label_values(pd_cluster_status{instance=\"$instance\"}, namespace)", + "refresh": 1, + "regex": "", + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "TIDB-Cluster-Overview", + "version": 6 +} \ No newline at end of file diff --git a/docker/tidb/config/dashboards/pd.json b/docker/tidb/config/dashboards/pd.json new file mode 100755 index 0000000..14f5158 --- /dev/null +++ b/docker/tidb/config/dashboards/pd.json @@ -0,0 +1,5999 @@ +{ + "__requires": [ + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "singlestat", + "name": "Singlestat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "tidb-cluster", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": false, + "gnetId": null, + "graphTooltip": 1, + "hideControls": false, + "id": null, + "links": [], + "refresh": "30s", + "rows": [ + { + "collapse": false, + "height": 299, + "panels": [ + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "tidb-cluster", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 55, + "interval": null, + "links": [], + "mappingType": 2, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "1", + "text": "Leader", + "to": "100000" + }, + { + "from": "0", + "text": "Follower", + "to": "1" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "delta(pd_server_tso{type=\"save\",instance=\"$instance\"}[1m])", + "intervalFactor": 2, + "legendFormat": "", + "metric": "pd_server_tso", + "refId": "A", + "step": 40 + } + ], + "thresholds": "", + "title": "PD Role", + "type": "singlestat", + "valueFontSize": "50%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "tidb-cluster", + "decimals": null, + "editable": false, + "error": false, + "format": "decbytes", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": false + }, + "id": 10, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(77, 135, 25, 0.18)", + "full": true, + "lineColor": "rgb(21, 179, 65)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(pd_cluster_status{instance=\"$instance\",namespace=~\"$namespace\",type=\"storage_capacity\"})", + "intervalFactor": 2, + "refId": "A", + "step": 40 + } + ], + "thresholds": "", + "title": "Storage Capacity", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "format": "decbytes", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "hideTimeOverride": false, + "id": 38, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(pd_cluster_status{instance=\"$instance\",namespace=~\"$namespace\",type=\"storage_size\"})", + "intervalFactor": 2, + "refId": "A", + "step": 40 + } + ], + "thresholds": "", + "title": "Current Storage Size", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": false + }, + "id": 20, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(pd_cluster_status{instance=\"$instance\",namespace=~\"$namespace\",type=\"leader_count\"})", + "format": "time_series", + "intervalFactor": 2, + "refId": "A", + "step": 40 + } + ], + "thresholds": "", + "title": "Number of Regions", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": [ + "rgba(50, 172, 45, 0.97)", + "rgba(237, 129, 40, 0.89)", + "rgba(245, 54, 54, 0.9)" + ], + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "format": "percentunit", + "gauge": { + "maxValue": 1, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "hideTimeOverride": false, + "id": 37, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "1 - min(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_score\"}) / max(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_score\"})", + "format": "time_series", + "intervalFactor": 2, + "refId": "A", + "step": 40 + } + ], + "thresholds": "0.01,0.5", + "title": "Leader Balance Ratio", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": [ + "rgba(50, 172, 45, 0.97)", + "rgba(237, 129, 40, 0.89)", + "rgba(245, 54, 54, 0.9)" + ], + "datasource": "tidb-cluster", + "decimals": null, + "editable": false, + "error": false, + "format": "percentunit", + "gauge": { + "maxValue": 1, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 36, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "1 - min(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_score\"}) / max(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_score\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A", + "step": 40 + } + ], + "thresholds": "0.05,0.5", + "title": "Region Balance Ratio", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#d44a3a", + "rgba(237, 129, 40, 0.89)", + "#299c46" + ], + "datasource": "tidb-cluster", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 97, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 3, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_up_count\"})", + "format": "time_series", + "intervalFactor": 2, + "refId": "A" + } + ], + "thresholds": "0,1", + "title": "Normal Stores", + "type": "singlestat", + "valueFontSize": "100%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "columns": [ + { + "text": "Current", + "value": "current" + } + ], + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fontSize": "100%", + "hideTimeOverride": true, + "id": 96, + "links": [], + "pageSize": null, + "scroll": false, + "showHeader": true, + "sort": { + "col": null, + "desc": false + }, + "span": 3, + "styles": [ + { + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Metric", + "sanitize": false, + "type": "string" + }, + { + "colorMode": "cell", + "colors": [ + "rgba(50, 172, 45, 0.97)", + "rgba(237, 129, 40, 0.89)", + "rgba(245, 54, 54, 0.9)" + ], + "decimals": 0, + "pattern": "Current", + "thresholds": [ + "1", + "2" + ], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_disconnected_count\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Disconnect Stores", + "refId": "B", + "step": 20 + }, + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_unhealth_count\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Unhealth Stores", + "refId": "C", + "step": 20 + }, + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_low_space_count\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "LowSpace Stores", + "refId": "D", + "step": 20 + }, + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_down_count\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Down Stores", + "refId": "E", + "step": 20 + }, + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_offline_count\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Offline Stores", + "refId": "F", + "step": 20 + }, + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_tombstone_count\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Tombstone Stores", + "refId": "G", + "step": 20 + } + ], + "timeFrom": "1s", + "title": "Abnormal Stores", + "transform": "timeseries_aggregations", + "transparent": false, + "type": "table" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 2, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 9, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "used ratio", + "yaxis": 2 + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"storage_size\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "storage size", + "refId": "A", + "step": 4 + }, + { + "expr": "avg(pd_cluster_status{type=\"storage_size\", namespace=~\"$namespace\"}) / avg(pd_cluster_status{type=\"storage_capacity\", namespace=~\"$namespace\"})", + "format": "time_series", + "hide": false, + "intervalFactor": 4, + "legendFormat": "used ratio", + "refId": "B", + "step": 8 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Current Storage Usage", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": null, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 18, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_count\"})", + "intervalFactor": 2, + "legendFormat": "count", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Current Peer Count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 76, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_cluster_metadata{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Metadata Information", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 75, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_regions_label_level{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region Label Isolation Level", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 100 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "60s", + "handler": 1, + "message": "Regions are unhealthy", + "name": "region health alert", + "noDataState": "keep_state", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 72, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_regions_status{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + }, + { + "expr": "sum(pd_regions_status) by (instance, type)", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 100 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Region Health", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Cluster", + "titleSize": "h4" + }, + { + "collapse": true, + "height": 214, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 83, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_capacity\"}", + "format": "time_series", + "hide": false, + "instant": false, + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store capacity", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 91, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_available\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store available", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 90, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_used\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store used", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 85, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_size\"}) by (store) / sum(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_used\"}) by (store) * 2^20", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Size amplification", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 84, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_available\"}) by (store) / sum(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_capacity\"}) by (store)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store available ratio", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": null, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 40, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_score\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 1000000000 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Store leader score", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": null, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 41, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_score\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 1000000000 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Store region score", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 56, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_size\"}", + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store leader size", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decmbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 57, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_size\"}", + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store region size", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decmbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 58, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_count\"}", + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store leader count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 59, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_count\"}", + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store region count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Balance", + "titleSize": "h4" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 0, + "fill": 0, + "id": 50, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_write_region_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot write region's leader distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 0, + "fill": 0, + "id": 51, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_write_region_as_peer\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot write region's peer distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": null, + "fill": 1, + "id": 48, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"total_written_bytes_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "metric": "pd_hotspot_status", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot write region's leader written bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": null, + "fill": 1, + "id": 49, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"total_written_bytes_as_peer\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot region's peer written bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 0, + "fill": 0, + "id": 60, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_read_region_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot read region's leader distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 0, + "fill": 0, + "id": 61, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_read_region_as_peer\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot read region's peer distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 62, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"total_read_bytes_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "metric": "pd_hotspot_status", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot read region's leader read bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 63, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"total_read_bytes_as_peer\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot read region's peer read bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "HotRegion", + "titleSize": "h4" + }, + { + "collapse": true, + "height": 288, + "panels": [ + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 0, + "id": 46, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 1, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_status{type=\"allow\",instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{kind}}", + "metric": "pd_scheduler_status", + "refId": "A", + "step": 2 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler is running", + "tooltip": { + "shared": true, + "sort": 1, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 87, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "total", + "sortDesc": true, + "total": true, + "values": true + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_balance_leader{instance=\"$instance\", type=\"move_leader\"}[30s])) by (store)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance leader movement", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "hideTimeOverride": false, + "id": 86, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "total", + "sortDesc": true, + "total": true, + "values": true + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_balance_region{instance=\"$instance\", type=\"move_peer\"}[1m])) by (store)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance region movement", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 89, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": true, + "values": true + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_balance_leader{instance=\"$instance\", type!=\"move_leader\"}[30s])) by (type, store)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}_{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance leader event", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 88, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sortDesc": false, + "total": true, + "values": true + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_balance_region{instance=\"$instance\", type!=\"move_peer\"}[30s])) by (type, store)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}_{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance region event", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 52, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_event_count{instance=\"$instance\", type=\"balance-leader-scheduler\"}[5m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "pd_scheduler_event_count", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance leader scheduler", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 53, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_event_count{instance=\"$instance\", type=\"balance-region-scheduler\"}[5m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "pd_scheduler_event_count", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance region scheduler", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 69, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_checker_event_count{instance=\"$instance\", type=\"namespace_checker\"}[1m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Namespace checker", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 70, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_checker_event_count{instance=\"$instance\", type=\"replica_checker\"}[1m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Replica checker", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 71, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_checker_event_count{instance=\"$instance\", type=\"merge_checker\"}[1m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region merge checker", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Scheduler", + "titleSize": "h4" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 45, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"create\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operator create", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 79, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"check\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operator check", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 77, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"finish\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operator finish", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 78, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"timeout\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operator timeout", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 80, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"canceled\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"replaced\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operator replaced or canceled", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 0, + "id": 47, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\"}[1m])) by (event)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{event}}", + "metric": "pd_scheduler_status", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operators count by state", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 67, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(pd_schedule_finish_operators_duration_seconds_bucket[5m])) by (type, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% operator finish duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 68, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.5, sum(rate(pd_schedule_finish_operators_duration_seconds_bucket[5m])) by (type, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "50% operator finish duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 81, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(pd_schedule_finish_operator_steps_duration_seconds_bucket[5m])) by (type, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% operator step finish duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "dtdurations", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 82, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.5, sum(rate(pd_schedule_finish_operator_steps_duration_seconds_bucket[5m])) by (type, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "50% operator step finish duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "dtdurations", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Operator", + "titleSize": "h4" + }, + { + "collapse": true, + "height": "300px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": null, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 1, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(grpc_server_handling_seconds_count{instance=\"$instance\"}[1m])) by (grpc_method)", + "intervalFactor": 2, + "legendFormat": "{{grpc_method}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "completed commands rate", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 10, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 2, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(grpc_server_handling_seconds_bucket{instance=\"$instance\"}[5m])) by (grpc_method, le))", + "intervalFactor": 2, + "legendFormat": "{{grpc_method}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% completed_cmds_duration_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Grpc", + "titleSize": "h4" + }, + { + "collapse": true, + "height": "300px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 5, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_txn_handle_txns_duration_seconds_count[5m])) by (instance, result)", + "intervalFactor": 2, + "legendFormat": "{{instance}} : {{result}}", + "refId": "A", + "step": 2 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "handle_txns_count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 6, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(pd_txn_handle_txns_duration_seconds_bucket[5m])) by (instance, result, le))", + "intervalFactor": 2, + "legendFormat": "{{instance}} {{result}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% handle_txns_duration_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 7, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m])) by (instance, le))", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% wal_fsync_duration_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 34, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(etcd_network_peer_round_trip_time_seconds_bucket[5m])) by (instance, To, le))", + "intervalFactor": 2, + "legendFormat": "{{instance}} - {{To}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% peer_round_trip_time_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 0.1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "datasourceId": 1, + "model": { + "expr": "delta(etcd_disk_wal_fsync_duration_seconds_count[1m])", + "intervalFactor": 2, + "legendFormat": "{{instance}} etch disk wal fsync rate", + "refId": "A", + "step": 4 + }, + "params": [ + "A", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "60s", + "handler": 1, + "message": "PD etcd disk fsync maybe is down.", + "name": "etcd disk fsync", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 44, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "delta(etcd_disk_wal_fsync_duration_seconds_count[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} etch disk wal fsync rate", + "refId": "A", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 0.1 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "etcd disk wal fsync rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 92, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_server_etcd_state{type=\"term\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - {{job}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Raft Term", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 93, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_server_etcd_state{type=\"committedIndex\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - {{job}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Raft Committed Index", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 94, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_server_etcd_state{type=\"appliedIndex\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - {{job}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Raft Applied Index", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Etcd", + "titleSize": "h4" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 28, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_client_request_handle_requests_duration_seconds_count[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 2 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "handle_requests_count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 29, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "sort": "current", + "sortDesc": false, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.98, sum(rate(pd_client_request_handle_requests_duration_seconds_bucket[30s])) by (type, le))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{type}} 98th percentile", + "refId": "A", + "step": 2 + }, + { + "expr": "avg(rate(pd_client_request_handle_requests_duration_seconds_sum[30s])) by (type) / avg(rate(pd_client_request_handle_requests_duration_seconds_count[30s])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}} average", + "refId": "B", + "step": 2 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "handle_requests_duration_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "TiDB", + "titleSize": "h4" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 54, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_region_heartbeat{instance=\"$instance\", type=\"report\", status=\"ok\"}[1m])) by (store)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region heartbeat report", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 77, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_region_heartbeat{instance=\"$instance\", type=\"report\", status=\"err\"}[1m])) by (store)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region heartbeat report error", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 78, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_region_heartbeat{instance=\"$instance\", type=\"report\", status=\"bind\"}[1m])) by (store)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region heartbeat report active", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 64, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_scheduler_region_heartbeat{type=\"push\",instance=\"$instance\"}[5m])*60) by (store, status)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "store{{store}}-{{status}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region schedule push", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 74, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "round(histogram_quantile(0.99, sum(rate(pd_scheduler_region_heartbeat_latency_seconds_bucket[5m])) by (store, le)), 1000)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% region heartbeat latency", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Heartbeat", + "titleSize": "h4" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "tidb-cluster", + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "instance", + "options": [], + "query": "label_values(pd_cluster_status, instance)", + "refresh": 1, + "regex": "", + "sort": 0, + "tagValuesQuery": null, + "tags": [], + "tagsQuery": null, + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": {}, + "datasource": "tidb-cluster", + "hide": 0, + "includeAll": true, + "label": "Namespace", + "multi": false, + "name": "namespace", + "options": [], + "query": "label_values(pd_cluster_status{instance=\"$instance\"}, namespace)", + "refresh": 1, + "regex": "", + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "TIDB-Cluster-PD", + "version": 55 +} diff --git a/docker/tidb/config/dashboards/tidb.json b/docker/tidb/config/dashboards/tidb.json new file mode 100755 index 0000000..8117982 --- /dev/null +++ b/docker/tidb/config/dashboards/tidb.json @@ -0,0 +1,6996 @@ +{ + "__requires": [ + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "tidb-cluster", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": false, + "gnetId": null, + "graphTooltip": 0, + "hideControls": false, + "id": null, + "links": [], + "refresh": "30s", + "rows": [ + { + "collapse": false, + "height": "240", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 80, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "95", + "refId": "C" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 42, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": false, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_server_query_total[1m])) by (result)", + "format": "time_series", + "instant": false, + "intervalFactor": 2, + "legendFormat": "query {{result}}", + "refId": "A", + "step": 60 + }, + { + "expr": "sum(rate(tidb_server_query_total{result=\"OK\"}[1m] offset 1d))", + "format": "time_series", + "hide": true, + "instant": false, + "intervalFactor": 3, + "legendFormat": "yesterday", + "refId": "B", + "step": 90 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "QPS", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": null, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 21, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_executor_statement_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Statement OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 2, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(tidb_server_query_total[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} {{type}} {{result}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "QPS By Instance", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 92, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_server_execute_error_total[1m])) by (type, instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": " {{type}}-{{instance}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Failed Query OPM", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 112, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_server_slow_query_process_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "all_proc", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_server_slow_query_cop_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "all_cop_proc", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_server_slow_query_wait_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "all_cop_wait", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Slow query", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Query Summary", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 23, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "B", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration 80 By Instance", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 1, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ instance }}", + "refId": "B", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration 95 By Instance", + "tooltip": { + "msResolution": true, + "shared": false, + "sort": 0, + "value_type": "cumulative" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + "max" + ] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 25, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration 99 By Instance", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 81, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration 999 By Instance", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 94, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "increase(tidb_server_execute_error_total[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}} @ {{instance}}", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Failed Query OPM Detail", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "description": "The internal SQL is used by TiDB itself.", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 68, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_session_restricted_sql_total[30s]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Internal SQL OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Query Detail", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 8, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "fill": 0, + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": true, + "targets": [ + { + "expr": "tidb_server_connections", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 40 + }, + { + "expr": "sum(tidb_server_connections)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "total", + "refId": "B", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Connection Count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 3, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "go_memstats_heap_inuse_bytes{job=~\"tidb.*\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "go_memstats_heap_inuse_bytes", + "refId": "B", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Heap Memory Usage", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 61, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": " go_goroutines{job=~\"tidb.*\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Goroutine Count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "description": "TiDB Server events.", + "fill": 1, + "id": 49, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "increase(tidb_server_event_total[10m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Events OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 54, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "increase(tidb_server_panic_total[1m])", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "panic-{{instance}}", + "refId": "A" + }, + { + "expr": "increase(tidb_server_critical_error_total[1m])", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "critical-{{instance}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Uncommon Error OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 82, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_monitor_keep_alive_total[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Keep Alive OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "description": "Duration (us) for getting token, it should be small until concurrency limit is reached.", + "fill": 1, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 111, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_server_get_token_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "99", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Get Token Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Server", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 69, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_session_transaction_total[1m])) by (type, sql_type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}-{{sql_type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Transaction OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 72, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le, sql_type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99-{{sql_type}}", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le, sql_type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "95-{{sql_type}}", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le, sql_type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80-{{sql_type}}", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 74, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_session_transaction_statement_num_bucket[30s])) by (le, sql_type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99-{{sql_type}}", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_session_transaction_statement_num_bucket[30s])) by (le, sql_type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80-{{sql_type}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Transaction Statement Num", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 36, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_session_retry_error_total[30s])) by (type, sql_type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}-{{sql_type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Session Retry Error OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 67, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1.0, sum(rate(tidb_session_retry_num_bucket[30s])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "100", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_session_retry_num_bucket[30s])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_session_retry_num_bucket[30s])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Transaction Retry Num", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Transaction", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 301, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": null, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 76, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_executor_expensive_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Expensive Executors OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": null, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 91, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_server_plan_cache_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Queries Using Plan Cache OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Executor", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 12, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [ + { + "type": "dashboard" + } + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_distsql_handle_query_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999-{{type}}", + "refId": "D" + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_distsql_handle_query_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "99-{{type}}", + "metric": "tidb_distsql_handle_query_duration_seconds_bucket", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_distsql_handle_query_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90-{{type}}", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(tidb_distsql_handle_query_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "50-{{type}}", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Distsql Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.0005", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_distsql_handle_query_duration_seconds_count[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "metric": "tidb_distsql_query_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Distsql QPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 60, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_distsql_scan_keys_partial_num_count[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "metric": "tidb_distsql_query_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Distsql Partial QPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 57, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_distsql_scan_keys_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "100", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_distsql_scan_keys_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(tidb_distsql_scan_keys_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scan Keys Num", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 58, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_distsql_scan_keys_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "100", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_distsql_scan_keys_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_distsql_scan_keys_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scan Keys Partial Num", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 59, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_distsql_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "100", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_distsql_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(tidb_distsql_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Partial Num", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 41, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_tikvclient_cop_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Coprocessor Seconds 999", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Distsql", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 6, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tidb_tikvclient_backoff_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "9999", + "refId": "A", + "step": 40 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_tikvclient_backoff_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_tikvclient_backoff_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Retry Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 2, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 11, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_region_err_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tidb_server_session_execute_parse_duration_count", + "refId": "A", + "step": 40 + }, + { + "expr": "sum(rate(tidb_tikvclient_region_err_total{type=\"server_is_busy\"}[1m]))", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "sum", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "TiClient Region Error OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 53, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": true, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_backoff_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Backoff OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 32, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_lock_resolver_actions_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tidb_tikvclient_lock_resolver_actions_total", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Lock Resolve OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 84, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_lock_cleanup_task_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "cleanup_secondary_failure_{{type}}", + "metric": "tidb_tikvclient_lock_resolver_actions_total", + "refId": "A", + "step": 40 + }, + { + "expr": "sum(rate(tidb_tikvclient_load_safepoint_total{type=\"fail\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "load_safepoint_failure", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Other Errors OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "KV Errors", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 48, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tidb_tikvclient_request_seconds_bucket{type!=\"GC\"}[1m])) by (le, store))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Request Duration 999 by store", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 30, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_tikvclient_request_seconds_bucket{type!=\"GC\"}[1m])) by (le,type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Request Duration 999 by type", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 22, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_tikvclient_txn_cmd_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Cmd Duration 9999", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 18, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_tikvclient_txn_cmd_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Cmd Duration 99", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "KV Duration", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 5, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_txn_cmd_duration_seconds_count[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Cmd OPS", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 4, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_txn_total[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Txn OPS", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 44, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_tikvclient_txn_regions_num_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Txn Regions Num 90", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 34, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "avg", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_tikvclient_txn_write_size_bytes_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Txn Write Size Bytes 100", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 33, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "avg", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_tikvclient_txn_write_kv_num_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "100 {{instance}}", + "refId": "B", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Txn Write KV Num 100", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 83, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "avg", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_load_safepoint_total{type=\"ok\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "B", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Load Safepoint OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "KV Count", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 20, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_client_cmd_handle_cmds_duration_seconds_count{type!=\"tso\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD Client CMD OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "description": "", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 35, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type!=\"tso\"}[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999-{{type}}", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type!=\"tso\"}[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99-{{type}}", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type!=\"tso\"}[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90-{{type}}", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD Client CMD Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 43, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_client_cmd_handle_failed_cmds_duration_seconds_count[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD Client CMD Fail OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "description": "The duration of a client calling GetTSAsync until received the TS result.", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 79, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_client_cmd_handle_cmds_duration_seconds_count{type=\"tso\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "cmd", + "refId": "C" + }, + { + "expr": "sum(rate(pd_client_request_handle_requests_duration_seconds_count{type=\"tso\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "request", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD TSO OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "description": "The duration of a client calling GetTSAsync until received the TS result.", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 77, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD TSO Wait Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "description": "The duration of a client sending TSO request until received the response.", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 78, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(pd_client_request_handle_requests_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(pd_client_request_handle_requests_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(pd_client_request_handle_requests_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD TSO RPC Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "PD Client", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 27, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_domain_load_schema_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Load Schema Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 28, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "/.*failed/", + "bars": true + } + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_domain_load_schema_total[1m])) by (instance,type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{type}}", + "metric": "tidb_domain_load_schema_duration_count", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Load Schema OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 2, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 29, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_session_schema_lease_error_total[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tidb_server_", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schema Lease Error OPM", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Schema Load", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 9, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_ddl_handle_job_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "DDL Duration 95", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 63, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_ddl_batch_add_idx_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Batch Add Index Duration 100", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 62, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "tidb_ddl_waiting_jobs", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "DDL Waiting Jobs Count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 55, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "increase(tidb_ddl_worker_operation_total[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "DDL OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 56, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(increase(tidb_ddl_worker_operation_duration_seconds_bucket[1m])) by (le, type, action, result))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}-{{action}}-{{result}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "DDL Worker Duration 99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 64, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_ddl_deploy_syncer_duration_seconds_bucket[2m])) by (le, type, result))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}-{{result}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Deploy Syncer Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 65, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_ddl_owner_handle_syncer_duration_seconds_bucket[2m])) by (le, type, result))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}-{{result}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Owner Handle Syncer Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 66, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_ddl_update_self_ver_duration_seconds_bucket[2m])) by (le, result))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{result}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Update Self Version Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "DDL", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 46, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_statistics_auto_analyze_duration_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "auto analyze duration", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Auto Analyze Duration 95", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 47, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_statistics_auto_analyze_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Auto Analyze QPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 70, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_statistics_stats_inaccuracy_rate_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "A", + "step": 30 + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_statistics_stats_inaccuracy_rate_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(tidb_statistics_stats_inaccuracy_rate_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Stats Inaccuracy Rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 71, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_statistics_pseudo_estimation_total[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Pseudo Estimation OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 92, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_statistics_dump_feedback_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Dump Feedback OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 93, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_statistics_update_stats_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Update Stats OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Statistics", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 50, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_autoid_operation_duration_seconds_count[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "AutoID QPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 51, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_autoid_operation_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99-{{type}}", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_autoid_operation_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80-{{type}}", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "AutoID Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 52, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_meta_operation_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Meta Operations Duration 99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Meta", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 85, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_tikvclient_gc_worker_actions_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Worker Action OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 86, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_tikvclient_gc_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration 99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 87, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "max(tidb_tikvclient_gc_config) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Config", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 88, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_tikvclient_gc_failure[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GC Failure OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 89, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_tikvclient_gc_action_result[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Action Result OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 90, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_tikvclient_gc_region_too_many_locks[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Too Many Locks Error OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "GC", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "TIDB-Cluster-TiDB", + "version": 3 +} diff --git a/docker/tidb/config/dashboards/tikv_pull.json b/docker/tidb/config/dashboards/tikv_pull.json new file mode 100755 index 0000000..02c4d52 --- /dev/null +++ b/docker/tidb/config/dashboards/tikv_pull.json @@ -0,0 +1,15500 @@ +{ + "__requires": [ + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "singlestat", + "name": "Singlestat", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "tidb-cluster", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": false, + "gnetId": null, + "graphTooltip": 1, + "hideControls": false, + "id": null, + "links": [], + "refresh": "1m", + "rows": [ + { + "collapse": false, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 5, + "grid": {}, + "id": 56, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 0, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_size_bytes{instance=~\"$instance\"}) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 5, + "grid": {}, + "id": 1706, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 0, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_store_size_bytes{instance=~\"$instance\", type=\"available\"}) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Available size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 5, + "grid": {}, + "id": 1707, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 0, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_store_size_bytes{instance=~\"$instance\", type=\"capacity\"}) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Capacity size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1708, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{instance=~\"$instance\"}[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1709, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(process_resident_memory_bytes{instance=~\"$instance\"}) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memory", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1710, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(node_disk_io_time_ms[1m]) / 1000", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - {{device}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "IO utilization", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1711, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_flow_bytes{instance=~\"$instance\", db=\"kv\", type=\"wal_file_bytes\"}[1m])) by (instance)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{instance}}-write", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{instance=~\"$instance\", db=\"kv\", type=~\"bytes_read|iter_bytes_read\"}[1m])) by (instance)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{instance}}-read", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "MBps", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1713, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": false, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_duration_seconds_count{instance=~\"$instance\", type!=\"kv_gc\"}[1m])) by (instance,type)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{instance}} - {{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "QPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1712, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_fail_total{instance=~\"$instance\", type!=\"kv_gc\"}[1m])) by (instance)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{instance}}-grpc-msg-fail", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(delta(tikv_pd_heartbeat_message_total{instance=~\"$instance\", type=\"noop\"}[1m])) by (instance) < 1", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-pd-heartbeat", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Errps", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1715, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{instance=~\"$instance\", type=\"leader\"}) by (instance)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 10 + }, + { + "expr": "delta(tikv_pd_heartbeat_tick_total{instance=~\"$instance\", type=\"leader\"}[30s]) < -10", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Leader", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1714, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{instance=~\"$instance\", type=\"region\"}) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Cluster", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1584, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_too_busy_total{instance=~\"$instance\"}[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "scheduler-{{instance}}", + "metric": "", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(rate(tikv_channel_full_total{instance=~\"$instance\"}[1m])) by (instance, type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "channelfull-{{instance}}-{{type}}", + "metric": "", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_coprocessor_request_error{instance=~\"$instance\", type='full'}[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "coprocessor-{{instance}}", + "metric": "", + "refId": "C", + "step": 4 + }, + { + "expr": "avg(tikv_engine_write_stall{instance=~\"$instance\", type=\"write_stall_percentile99\"}) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "stall-{{instance}}", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Server is busy", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "10s", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "10s", + "handler": 1, + "message": "TiKV server report failures", + "name": "server report failures alert", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 18, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_server_report_failure_msg_total{instance=~\"$instance\"}[1m])) by (type,instance,store_id)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - {{type}} - to - {{store_id}}", + "metric": "tikv_server_raft_store_msg_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Server report failures", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1718, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_storage_engine_async_request_total{instance=~\"$instance\", status!~\"success|all\"}[1m])) by (instance, status)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{status}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Raftstore error", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1719, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_stage_total{instance=~\"$instance\", stage=~\"snapshot_err|prepare_write_err\"}[1m])) by (instance, stage)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{stage}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler error", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1720, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_request_error{instance=~\"$instance\"}[1m])) by (instance, reason)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{reason}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Coprocessor error", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1721, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_fail_total{instance=~\"$instance\"}[1m])) by (instance, type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{type}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "gRPC message error", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1722, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(tikv_pd_heartbeat_tick_total{instance=~\"$instance\", type=\"leader\"}[1m])) by (instance)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Leader drop", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1723, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_raftstore_leader_missing{instance=~\"$instance\"}) by (instance)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Leader missing", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Errors", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 34, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{instance=~\"$instance\", type=\"leader\"}) by (instance)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 10 + }, + { + "expr": "delta(tikv_pd_heartbeat_tick_total{instance=~\"$instance\", type=\"leader\"}[30s]) < -10", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Leader", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 37, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{instance=~\"$instance\", type=\"region\"}) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 3, + "grid": {}, + "id": 33, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_size_bytes{instance=~\"$instance\"}) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "CF size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 5, + "grid": {}, + "id": 1705, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 0, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_size_bytes{instance=~\"$instance\"}) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "datasourceId": 1, + "model": { + "expr": "sum(rate(tikv_channel_full_total{instance=~\"$instance\"}[1m])) by (instance, type)", + "intervalFactor": 2, + "legendFormat": "{{instance}} - {{type}}", + "metric": "", + "refId": "A", + "step": 10 + }, + "params": [ + "A", + "10s", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "10s", + "handler": 1, + "message": "TiKV channel full", + "name": "TiKV channel full alert", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 3, + "grid": {}, + "id": 22, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_channel_full_total{instance=~\"$instance\"}[1m])) by (instance, type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - {{type}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Channel full", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1717, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_server_report_failure_msg_total{instance=~\"$instance\"}[1m])) by (type,instance,store_id)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - {{type}} - to - {{store_id}}", + "metric": "tikv_server_raft_store_msg_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Server report failures", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 57, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_region_written_keys_sum{instance=~\"$instance\"}[1m])) by (instance) / sum(rate(tikv_region_written_keys_count{instance=~\"$instance\"}[1m])) by (instance)", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_region_written_keys_bucket", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region average written keys", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 58, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_region_written_bytes_sum{instance=~\"$instance\"}[1m])) by (instance) / sum(rate(tikv_region_written_bytes_count{instance=~\"$instance\"}[1m])) by (instance)", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_regi", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region average written bytes", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 75, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_region_written_keys_count{instance=~\"$instance\"}[1m])) by (instance)", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_region_written_keys_bucket", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Active written leaders", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 1073741824 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "60s", + "handler": 1, + "name": "approximate region size alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1481, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_region_size_bucket{instance=~\"$instance\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_region_size_bucket{instance=~\"$instance\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_raftstore_region_size_sum{instance=~\"$instance\"}[1m])) / sum(rate(tikv_raftstore_region_size_count{instance=~\"$instance\"}[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Approximate Region size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Server", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 31, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_apply_log_duration_seconds_bucket{instance=~\"$instance\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " 99%", + "metric": "", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_apply_log_duration_seconds_bucket{instance=~\"$instance\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_raftstore_apply_log_duration_seconds_sum{instance=~\"$instance\"}[1m])) / sum(rate(tikv_raftstore_apply_log_duration_seconds_count{instance=~\"$instance\"}[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Apply log duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 32, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_apply_log_duration_seconds_bucket{instance=~\"$instance\"}[1m])) by (le, instance))", + "intervalFactor": 2, + "legendFormat": " {{instance}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Apply log duration per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 39, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_append_log_duration_seconds_bucket{instance=~\"$instance\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " 99%", + "metric": "", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_append_log_duration_seconds_bucket{instance=~\"$instance\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_raftstore_append_log_duration_seconds_sum{instance=~\"$instance\"}[1m])) / sum(rate(tikv_raftstore_append_log_duration_seconds_count{instance=~\"$instance\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Append log duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 40, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_append_log_duration_seconds_bucket{instance=~\"$instance\"}[1m])) by (le, instance))", + "intervalFactor": 2, + "legendFormat": "{{instance}} ", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Append log duration per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Raft IO", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 5, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_raft_ready_handled_total{instance=~\"$instance\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_raftstore_raft_ready_handled_total", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(rate(tikv_raftstore_raft_process_duration_secs_count{instance=~\"$instance\", type=\"ready\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "count", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Ready handled", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 118, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_raft_process_duration_secs_bucket{instance=~\"$instance\", type='ready'}[1m])) by (le, instance))", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "C", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 1 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Process ready duration per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1165, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_raft_process_duration_secs_bucket{instance=~\"$instance\", type='tick'}[1m])) by (le, instance))", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "C", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 1 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Process tick duration per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 123, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_event_duration_bucket{instance=~\"$instance\"}[1m])) by (le, type))", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "C", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 1 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "0.99 Duration of raft store events", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Raft process", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 1615, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_raft_sent_message_total{instance=~\"$instance\"}[1m])) by (instance)", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Sent messages per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 1616, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_server_raft_message_flush_total{instance=~\"$instance\"}[1m])) by (instance)", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_server_raft_message_flush_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Flush messages per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 106, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_server_raft_message_recv_total{instance=~\"$instance\"}[1m])) by (instance)", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Receive messages per server", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 11, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_raft_sent_message_total{instance=~\"$instance\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Messages", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 25, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_raft_sent_message_total{instance=~\"$instance\", type=\"vote\"}[1m])) by (instance)", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Vote", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 1309, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_raft_dropped_message_total{instance=~\"$instance\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Raft dropped messages", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Raft message", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 108, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_apply_proposal_bucket{instance=~\"$instance\"}[1m])) by (le, instance))", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Raft proposals per ready", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 7, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_proposal_total{instance=~\"$instance\", type=~\"local_read|normal|read_index\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_raftstore_proposal_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Raft read/write proposals", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 119, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_proposal_total{instance=~\"$instance\", type=~\"local_read|read_index\"}[1m])) by (instance)", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Raft read proposals per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 120, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_proposal_total{instance=~\"$instance\", type=\"normal\"}[1m])) by (instance)", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_raftstore_proposal_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Raft write proposals per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 41, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_request_wait_time_duration_secs_bucket{instance=~\"$instance\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "tikv_raftstore_request_wait_time_duration_secs_bucket", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_request_wait_time_duration_secs_bucket{instance=~\"$instance\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_raftstore_request_wait_time_duration_secs_sum{instance=~\"$instance\"}[1m])) / sum(rate(tikv_raftstore_request_wait_time_duration_secs_count{instance=~\"$instance\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Propose wait duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 42, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_request_wait_time_duration_secs_bucket{instance=~\"$instance\"}[1m])) by (le, instance))", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Propose wait duration per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 1975, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": true, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(rate(tikv_raftstore_propose_log_size_sum{instance=~\"$instance\"}[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Raft log speed", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "short", + "label": "bytes/s", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Raft propose", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 76, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_proposal_total{instance=~\"$instance\", type=~\"conf_change|transfer_leader\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_raftstore_proposal_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Admin proposals", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 77, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_admin_cmd_total{instance=~\"$instance\", status=\"success\", type!=\"compact\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_raftstore_admin_cmd_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Admin apply", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 70, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_check_split_total{instance=~\"$instance\", type!=\"ignore\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_raftstore_check_split_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Check split", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 71, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tikv_raftstore_check_split_duration_seconds_bucket{instance=~\"$instance\"}[1m])) by (le, instance))", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_raftstore_check_split_duration_seconds_bucket", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99.99% Check split duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Raft admin", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 2292, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "/.*-total/i", + "yaxis": 2 + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_local_read_reject_total{instance=~\"$instance\"}[1m])) by (instance, reason)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-reject-by-{{reason}}", + "refId": "A" + }, + { + "expr": "sum(rate(tikv_raftstore_local_read_batch_requests_total_sum{instance=~\"$instance\"}[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-total", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Local reader requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 2293, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_local_read_requests_wait_duration_bucket{instance=~\"$instance\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_local_read_requests_wait_duration_bucket{instance=~\"$instance\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B" + }, + { + "expr": "sum(rate(tikv_raftstore_local_read_requests_wait_duration_sum{instance=~\"$instance\"}[1m])) / sum(rate(tikv_raftstore_local_read_requests_wait_duration_count{instance=~\"$instance\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Local read requsets duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 2294, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_local_read_batch_requests_total_bucket{instance=~\"$instance\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_local_read_batch_requests_total_bucket{instance=~\"$instance\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B" + }, + { + "expr": "sum(rate(tikv_raftstore_local_read_batch_requests_total_sum{instance=~\"$instance\"}[1m])) / sum(rate(tikv_raftstore_local_read_batch_requests_total_count{instance=~\"$instance\"}[1m])) ", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Local read requests batch size", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Local reader", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 2, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_storage_command_total{instance=~\"$instance\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Storage command total", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 8, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_storage_engine_async_request_total{instance=~\"$instance\", status!~\"all|success\"}[1m])) by (status)", + "intervalFactor": 2, + "legendFormat": "{{status}}", + "metric": "tikv_raftstore_raft_process_duration_secs_bucket", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Storage async request error", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 15, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{instance=~\"$instance\", type=\"snapshot\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{instance=~\"$instance\", type=\"snapshot\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_engine_async_request_duration_seconds_sum{instance=~\"$instance\", type=\"snapshot\"}[1m])) / sum(rate(tikv_storage_engine_async_request_duration_seconds_count{instance=~\"$instance\", type=\"snapshot\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Storage async snapshot duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 109, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{instance=~\"$instance\", type=\"write\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{instance=~\"$instance\", type=\"write\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_engine_async_request_duration_seconds_sum{instance=~\"$instance\", type=\"write\"}[1m])) / sum(rate(tikv_storage_engine_async_request_duration_seconds_count{instance=~\"$instance\", type=\"write\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Storage async write duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 1310, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "raft-95%", + "yaxis": 2 + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_storage_batch_commands_total_bucket{instance=~\"$instance\"}[30s])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_storage_batch_commands_total_bucket{instance=~\"$instance\"}[30s])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_batch_commands_total_sum{instance=~\"$instance\"}[30s])) / sum(rate(tikv_storage_batch_commands_total_count{instance=~\"$instance\"}[30s]))", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_batch_snapshot_commands_total_bucket{instance=~\"$instance\"}[30s])) by (le))", + "intervalFactor": 2, + "legendFormat": "raft-95%", + "refId": "D", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "storage async batch snapshot", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "Storage Batch Size", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": "Raftstore Batch Size", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Storage", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "height": "400", + "id": 167, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_too_busy_total{instance=~\"$instance\"}[1m])) by (stage)", + "intervalFactor": 2, + "legendFormat": "busy", + "refId": "A", + "step": 20 + }, + { + "expr": "sum(rate(tikv_scheduler_stage_total{instance=~\"$instance\"}[1m])) by (stage)", + "intervalFactor": 2, + "legendFormat": "{{stage}}", + "refId": "B", + "step": 20 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler stage total", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "height": "", + "id": 1, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": 6, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_commands_pri_total{instance=~\"$instance\"}[1m])) by (priority)", + "intervalFactor": 2, + "legendFormat": "{{priority}}", + "metric": "", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler priority commands", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 300 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "120s", + "handler": 1, + "message": "TiKV scheduler context total", + "name": "scheduler pending commands alert", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "height": "", + "id": 193, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": 6, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_scheduler_contex_total{instance=~\"$instance\"}) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "", + "refId": "A", + "step": 40 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 300 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler pending commands", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Scheduler", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "height": "400", + "id": 168, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_too_busy_total{instance=~\"$instance\", type=\"$command\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "busy", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(rate(tikv_scheduler_stage_total{instance=~\"$instance\", type=\"$command\"}[1m])) by (stage)", + "intervalFactor": 2, + "legendFormat": "{{stage}}", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler stage total", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 3, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_scheduler_command_duration_seconds_bucket{instance=~\"$instance\", type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_scheduler_command_duration_seconds_bucket{instance=~\"$instance\", type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_scheduler_command_duration_seconds_sum{instance=~\"$instance\", type=\"$command\"}[1m])) / sum(rate(tikv_scheduler_command_duration_seconds_count{instance=~\"$instance\", type=\"$command\"}[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler command duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 194, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_scheduler_latch_wait_duration_seconds_bucket{instance=~\"$instance\", type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_scheduler_latch_wait_duration_seconds_bucket{instance=~\"$instance\", type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_scheduler_latch_wait_duration_seconds_sum{instance=~\"$instance\", type=\"$command\"}[1m])) / sum(rate(tikv_scheduler_latch_wait_duration_seconds_count{instance=~\"$instance\", type=\"$command\"}[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler latch wait duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 195, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_scheduler_kv_command_key_read_bucket{instance=~\"$instance\", type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "kv_command_key", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_scheduler_kv_command_key_read_bucket{instance=~\"$instance\", type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_scheduler_kv_command_key_read_sum{instance=~\"$instance\", type=\"$command\"}[1m])) / sum(rate(tikv_scheduler_kv_command_key_read_count{instance=~\"$instance\", type=\"$command\"}[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler keys read", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 373, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_scheduler_kv_command_key_write_bucket{instance=~\"$instance\", type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "kv_command_key", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_scheduler_kv_command_key_write_bucket{instance=~\"$instance\", type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_scheduler_kv_command_key_write_sum{instance=~\"$instance\", type=\"$command\"}[1m])) / sum(rate(tikv_scheduler_kv_command_key_write_count{instance=~\"$instance\", type=\"$command\"}[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler keys written", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 560, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_kv_scan_details{instance=~\"$instance\", req=\"$command\"}[1m])) by (tag)", + "intervalFactor": 2, + "legendFormat": "{{tag}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler scan details", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 675, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_kv_scan_details{instance=~\"$instance\", req=\"$command\", cf=\"lock\"}[1m])) by (tag)", + "intervalFactor": 2, + "legendFormat": "{{tag}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler scan details [lock]", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 829, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_kv_scan_details{instance=~\"$instance\", req=\"$command\", cf=\"write\"}[1m])) by (tag)", + "intervalFactor": 2, + "legendFormat": "{{tag}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler scan details [write]", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 830, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_kv_scan_details{instance=~\"$instance\", req=\"$command\", cf=\"default\"}[1m])) by (tag)", + "intervalFactor": 2, + "legendFormat": "{{tag}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler scan details [default]", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": "command", + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Scheduler - $command", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 5, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 16, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tikv_coprocessor_request_duration_seconds_bucket{instance=~\"$instance\"}[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-99.99%", + "refId": "E" + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_coprocessor_request_duration_seconds_bucket{instance=~\"$instance\"}[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_duration_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-95%", + "refId": "B", + "step": 4 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_duration_seconds_sum{instance=~\"$instance\"}[1m])) by (req) / sum(rate(tikv_coprocessor_request_duration_seconds_count{instance=~\"$instance\"}[1m])) by (req)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Request duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 5, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 111, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tikv_coprocessor_request_wait_seconds_bucket{instance=~\"$instance\"}[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-99.99%", + "refId": "D" + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_coprocessor_request_wait_seconds_bucket{instance=~\"$instance\"}[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_wait_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-95%", + "refId": "B", + "step": 4 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_wait_seconds_sum{instance=~\"$instance\"}[1m])) by (req) / sum(rate(tikv_coprocessor_request_wait_seconds_count{instance=~\"$instance\"}[1m])) by (req)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Wait duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 5, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 113, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tikv_coprocessor_request_handle_seconds_bucket{instance=~\"$instance\"}[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-99.99%", + "refId": "E" + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_coprocessor_request_handle_seconds_bucket{instance=~\"$instance\"}[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_handle_seconds_bucket{instance=~\"$instance\"}[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-95%", + "refId": "B", + "step": 4 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_handle_seconds_sum{instance=~\"$instance\"}[1m])) by (req) / sum(rate(tikv_coprocessor_request_handle_seconds_count{instance=~\"$instance\"}[1m])) by (req)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Handle duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 5, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 115, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_duration_seconds_bucket{instance=~\"$instance\"}[1m])) by (le, instance, req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{req}}", + "metric": "tikv_coprocessor_request_duration_seconds_bucket", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "95% Request duration by store", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 5, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 116, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_wait_seconds_bucket{instance=~\"$instance\"}[1m])) by (le, instance,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{req}}", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "95% Wait duration by store", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 5, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 117, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_handle_seconds_bucket{instance=~\"$instance\"}[1m])) by (le, instance,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{req}}", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "95% Handle duration by store", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 2, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 74, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_request_error{instance=~\"$instance\"}[1m])) by (reason)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{reason}}", + "metric": "tikv_coprocessor_request_error", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Request errors", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 551, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_executor_count{instance=~\"$instance\"}[1m])) by (type)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "DAG executors", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 52, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, avg(rate(tikv_coprocessor_scan_keys_bucket{instance=~\"$instance\"}[1m])) by (le, req)) ", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-99.99%", + "refId": "D" + }, + { + "expr": "histogram_quantile(0.99, avg(rate(tikv_coprocessor_scan_keys_bucket{instance=~\"$instance\"}[1m])) by (le, req)) ", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{req}}-99%", + "metric": "tikv_coprocessor_scan_keys_bucket", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, avg(rate(tikv_coprocessor_scan_keys_bucket{instance=~\"$instance\"}[1m])) by (le, req)) ", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-95%", + "metric": "tikv_coprocessor_scan_keys_bucket", + "refId": "B", + "step": 10 + }, + { + "expr": "histogram_quantile(0.90, avg(rate(tikv_coprocessor_scan_keys_bucket{instance=~\"$instance\"}[1m])) by (le, req)) ", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-90%", + "metric": "tikv_coprocessor_scan_keys_bucket", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scan keys", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 552, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_scan_details{instance=~\"$instance\"}[1m])) by (tag,req)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{req}}-{{tag}}", + "metric": "scan_details", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scan details", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 122, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_scan_details{instance=~\"$instance\", req=\"select\"}[1m])) by (tag,cf)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{cf}}-{{tag}}", + "metric": "scan_details", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Table Scan - Details by CF", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 554, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": "cf", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_scan_details{instance=~\"$instance\", req=\"index\"}[1m])) by (tag,cf)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{cf}}-{{tag}}", + "metric": "scan_details", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Index Scan - Details by CF", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "description": "RocksDB Internal Statistics for Table Scan Requests", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 2118, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "key_skipped", + "yaxis": 2 + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_rocksdb_perf{instance=~\"$instance\", req=\"select\",metric=\"internal_delete_skipped_count\"}[1m])) by (metric)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "delete_skipped", + "metric": "scan_details", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_coprocessor_rocksdb_perf{instance=~\"$instance\", req=\"select\",metric=\"internal_key_skipped_count\"}[1m])) by (metric)", + "format": "time_series", + "instant": false, + "intervalFactor": 2, + "legendFormat": "key_skipped", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Table Scan - Perf Statistics", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "decimals": null, + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "description": "RocksDB Internal Statistics for Index Scan Requests", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 2119, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "key_skipped", + "yaxis": 2 + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_rocksdb_perf{instance=~\"$instance\", req=\"index\",metric=\"internal_delete_skipped_count\"}[1m])) by (metric)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "delete_skipped", + "metric": "scan_details", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_coprocessor_rocksdb_perf{instance=~\"$instance\", req=\"index\",metric=\"internal_key_skipped_count\"}[1m])) by (metric)", + "format": "time_series", + "instant": false, + "intervalFactor": 2, + "legendFormat": "key_skipped", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Index Scan - Perf Statistics", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "decimals": null, + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Coprocessor", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 26, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1.0, sum(rate(tikv_storage_mvcc_versions_bucket{instance=~\"$instance\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " max", + "metric": "", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_storage_mvcc_versions_bucket{instance=~\"$instance\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "B", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_storage_mvcc_versions_bucket{instance=~\"$instance\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " 95%", + "metric": "", + "refId": "C", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_mvcc_versions_sum{instance=~\"$instance\"}[1m])) / sum(rate(tikv_storage_mvcc_versions_count{instance=~\"$instance\"}[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "D", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "MVCC versions", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 559, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1.0, sum(rate(tikv_storage_mvcc_gc_delete_versions_bucket{instance=~\"$instance\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " max", + "metric": "", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_storage_mvcc_gc_delete_versions_bucket{instance=~\"$instance\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "B", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_storage_mvcc_gc_delete_versions_bucket{instance=~\"$instance\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " 95%", + "metric": "", + "refId": "C", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_mvcc_gc_delete_versions_sum{instance=~\"$instance\"}[1m])) / sum(rate(tikv_storage_mvcc_gc_delete_versions_count{instance=~\"$instance\"}[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "D", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "MVCC delete versions", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 121, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_gcworker_gc_tasks_vec{instance=~\"$instance\"}[1m])) by (task)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "total-{{task}}", + "metric": "tikv_storage_command_total", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_gc_skipped_counter{instance=~\"$instance\"}[1m])) by (task)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "skipped-{{task}}", + "metric": "tikv_storage_gc_skipped_counter", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_gcworker_gc_task_fail_vec{instance=~\"$instance\"}[1m])) by (task)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "failed-{{task}}", + "refId": "C" + }, + { + "expr": "sum(rate(tikv_gc_worker_too_busy{instance=~\"$instance\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GC tasks", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 2224, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tikv_gcworker_gc_task_duration_vec_bucket{instance=~\"$instance\"}[1m])) by (le, task))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "max-{{task}}", + "metric": "tikv_storage_command_total", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_gcworker_gc_task_duration_vec_bucket{instance=~\"$instance\"}[1m])) by (le, task))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99%-{{task}}", + "metric": "tikv_storage_gc_skipped_counter", + "refId": "B", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_gcworker_gc_task_duration_vec_bucket{instance=~\"$instance\"}[1m])) by (le, task))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "95%-{{task}}", + "refId": "C" + }, + { + "expr": "sum(rate(tikv_gcworker_gc_task_duration_vec_sum{instance=~\"$instance\"}[1m])) by (task) / sum(rate(tikv_gcworker_gc_task_duration_vec_count{instance=~\"$instance\"}[1m])) by (task)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "average-{{task}}", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GC tasks duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 2225, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_gcworker_gc_keys{instance=~\"$instance\", cf=\"write\"}[1m])) by (tag)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{tag}}", + "metric": "tikv_storage_command_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GC keys (write CF)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 2, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 967, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_gc_action_result_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "TiDB GC actions result", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 2, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 966, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_gc_worker_actions_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "TiDB GC worker actions", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 969, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1.0, sum(rate(tidb_tikvclient_gc_seconds_bucket[1m])) by (instance, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "TiDB GC seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 2, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 968, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_gc_failure_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "TiDB GC failure", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "tidb-cluster", + "decimals": 0, + "editable": false, + "error": false, + "format": "s", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 27, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 3, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "max(tidb_tikvclient_gc_config{type=\"tikv_gc_life_time\"})", + "interval": "", + "intervalFactor": 2, + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "GC lifetime", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "tidb-cluster", + "decimals": 0, + "editable": false, + "error": false, + "format": "s", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 28, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 3, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "max(tidb_tikvclient_gc_config{type=\"tikv_gc_run_interval\"})", + "intervalFactor": 2, + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "GC interval", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "GC", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 35, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(tikv_raftstore_raft_sent_message_total{instance=~\"$instance\", type=\"snapshot\"}[1m]))", + "intervalFactor": 2, + "legendFormat": " ", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Rate snapshot message", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 36, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_server_send_snapshot_duration_seconds_bucket{instance=~\"$instance\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "send", + "refId": "A", + "step": 60 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_snapshot_duration_seconds_bucket{instance=~\"$instance\", type=\"apply\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "apply", + "refId": "B", + "step": 60 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_snapshot_duration_seconds_bucket{instance=~\"$instance\", type=\"generate\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "generate", + "refId": "C", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% Handle snapshot duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 38, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 4, + "stack": false, + "steppedLine": true, + "targets": [ + { + "expr": "sum(tikv_raftstore_snapshot_traffic_total{instance=~\"$instance\"}) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Snapshot state count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 44, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tikv_snapshot_size_bucket{instance=~\"$instance\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "size", + "metric": "tikv_snapshot_size_bucket", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99.99% Snapshot size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 43, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tikv_snapshot_kv_count_bucket{instance=~\"$instance\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "count", + "metric": "tikv_snapshot_kv_count_bucket", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99.99% Snapshot KV count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Snapshot", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 59, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 400, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_worker_handled_task_total{instance=~\"$instance\"}[1m])) by (name)", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Worker handled tasks", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 1395, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 400, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_worker_pending_task_total{instance=~\"$instance\"}[1m])) by (name)", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Worker pending tasks", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 1876, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 400, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_futurepool_handled_task_total{instance=~\"$instance\"}[1m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "FuturePool handled tasks", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 1877, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 400, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_futurepool_pending_task_total{instance=~\"$instance\"}[1m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "FuturePool pending tasks", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 2 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "10s", + "handler": 1, + "message": "coprocessor pending requests", + "name": "coprocessor pending requests alert", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 2, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 550, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_pending_request{instance=~\"$instance\"}[1m])) by (req, priority)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{ req }} - {{priority}}", + "metric": "tikv_coprocessor_request_error", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(rate(tikv_coprocessor_pending_request{instance=~\"$instance\"}[1m])) by (type, instance)", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 2 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor pending requests", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Task", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 0.8 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "datasourceId": 1, + "model": { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{instance=~\"$instance\", name=~\"raftstore_.*\"}[1m])) by (instance, name)", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 20 + }, + "params": [ + "A", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "60s", + "handler": 1, + "message": "TiKV raftstore thread CPU usage is high", + "name": "TiKV raft store CPU alert", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 61, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{instance=~\"$instance\", name=~\"raftstore_.*\"}[1m])) by (instance, name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0.8 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Raft store CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 1, + "grid": {}, + "id": 79, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{instance=~\"$instance\", name=~\"apply_worker\"}[1m])) by (instance)", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Async apply CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 2291, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{instance=~\"$instance\", name=~\"local_reader\"}[1m])) by (instance, name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Local reader CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 63, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{instance=~\"$instance\", name=~\"storage_schedul.*\"}[1m])) by (instance)", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 64, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{instance=~\"$instance\", name=~\"sched_.*\"}[1m])) by (instance)", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler worker CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 1908, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{instance=~\"$instance\", name=~\"store_read.*\"}[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Storage ReadPool CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 78, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{instance=~\"$instance\", name=~\"cop_.*\"}[1m])) by (instance)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Coprocessor CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 67, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{instance=~\"$instance\", name=~\"snapshot_worker\"}[1m])) by (instance)", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Snapshot worker CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 68, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{instance=~\"$instance\", name=~\"split_check\"}[1m])) by (instance)", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Split check CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "editable": false, + "error": false, + "fill": 0, + "grid": {}, + "id": 69, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{instance=~\"$instance\", name=~\"rocksdb.*\"}[1m])) by (instance)", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "warning", + "fill": true, + "line": true, + "op": "gt", + "value": 1 + }, + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 4 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "RocksDB CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 105, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{instance=~\"$instance\", name=~\"grpc.*\"}[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "gRPC poll CPU", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Thread CPU", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 2108, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": false, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 2, + "points": true, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_threads_state{instance=~\"$instance\"}) by (instance, state)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{instance}}-{{state}}", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(tikv_threads_state{instance=~\"$instance\"}) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-total", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Threads state", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 2258, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 2, + "points": true, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_threads_io_bytes_total{instance=~\"$instance\"}[30s])) by (name, io) > 1024", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{name}}-{{io}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Threads IO", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Threads", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 138, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_memtable_efficiency{instance=~\"$instance\", db=\"$db\", type=\"memtable_hit\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "memtable", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{instance=~\"$instance\", db=\"$db\", type=~\"block_cache_data_hit|block_cache_filter_hit\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "block_cache", + "metric": "", + "refId": "E", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_get_served{instance=~\"$instance\", db=\"$db\", type=\"get_hit_l0\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "l0", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_get_served{instance=~\"$instance\", db=\"$db\", type=\"get_hit_l1\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "l1", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_get_served{instance=~\"$instance\", db=\"$db\", type=\"get_hit_l2_and_up\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "l2_and_up", + "refId": "F", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Get operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 0, + "id": 82, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_get_micro_seconds{instance=~\"$instance\", db=\"$db\",type=\"get_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_get_micro_seconds{instance=~\"$instance\", db=\"$db\",type=\"get_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_get_micro_seconds{instance=~\"$instance\", db=\"$db\",type=\"get_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_get_micro_seconds{instance=~\"$instance\", db=\"$db\",type=\"get_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Get duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 129, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_locate{instance=~\"$instance\", db=\"$db\", type=\"number_db_seek\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "seek", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_locate{instance=~\"$instance\", db=\"$db\", type=\"number_db_seek_found\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "seek_found", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_locate{instance=~\"$instance\", db=\"$db\", type=\"number_db_next\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "next", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_locate{instance=~\"$instance\", db=\"$db\", type=\"number_db_next_found\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "next_found", + "metric": "", + "refId": "D", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_locate{instance=~\"$instance\", db=\"$db\", type=\"number_db_prev\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "prev", + "metric": "", + "refId": "E", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_locate{instance=~\"$instance\", db=\"$db\", type=\"number_db_prev_found\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "prev_found", + "metric": "", + "refId": "F", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Seek operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 0, + "id": 125, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_seek_micro_seconds{instance=~\"$instance\", db=\"$db\",type=\"seek_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_seek_micro_seconds{instance=~\"$instance\", db=\"$db\",type=\"seek_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_seek_micro_seconds{instance=~\"$instance\", db=\"$db\",type=\"seek_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_seek_micro_seconds{instance=~\"$instance\", db=\"$db\",type=\"seek_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Seek duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 139, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_write_served{instance=~\"$instance\", db=\"$db\", type=~\"write_done_by_self|write_done_by_other\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "done", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_write_served{instance=~\"$instance\", db=\"$db\", type=\"write_timeout\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "timeout", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_write_served{instance=~\"$instance\", db=\"$db\", type=\"write_with_wal\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "with_wal", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Write operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 0, + "id": 126, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_write_micro_seconds{instance=~\"$instance\", db=\"$db\",type=\"write_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_micro_seconds{instance=~\"$instance\", db=\"$db\",type=\"write_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_micro_seconds{instance=~\"$instance\", db=\"$db\",type=\"write_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_micro_seconds{instance=~\"$instance\", db=\"$db\",type=\"write_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Write duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 137, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_wal_file_synced{instance=~\"$instance\", db=\"$db\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "sync", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "WAL sync operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 0, + "id": 135, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 6, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_wal_file_sync_micro_seconds{instance=~\"$instance\", db=\"$db\",type=\"wal_file_sync_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_wal_file_sync_micro_seconds{instance=~\"$instance\", db=\"$db\",type=\"wal_file_sync_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_wal_file_sync_micro_seconds{instance=~\"$instance\", db=\"$db\",type=\"wal_file_sync_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_wal_file_sync_micro_seconds{instance=~\"$instance\", db=\"$db\",type=\"wal_file_sync_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "WAL sync duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 128, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_event_total{instance=~\"$instance\", db=\"$db\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_engine_event_total", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compaction operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 0, + "id": 136, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_compaction_time{instance=~\"$instance\", db=\"$db\",type=\"compaction_time_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_compaction_time{instance=~\"$instance\", db=\"$db\",type=\"compaction_time_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_compaction_time{instance=~\"$instance\", db=\"$db\",type=\"compaction_time_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_compaction_time{instance=~\"$instance\", db=\"$db\",type=\"compaction_time_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compaction duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 140, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_sst_read_micros{instance=~\"$instance\", db=\"$db\", type=\"sst_read_micros_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_sst_read_micros{instance=~\"$instance\", db=\"$db\", type=\"sst_read_micros_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_sst_read_micros{instance=~\"$instance\", db=\"$db\", type=\"sst_read_micros_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_sst_read_micros{instance=~\"$instance\", db=\"$db\", type=\"sst_read_micros_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "SST read duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 10, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 87, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_write_stall{instance=~\"$instance\", db=\"$db\", type=\"write_stall_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_stall{instance=~\"$instance\", db=\"$db\", type=\"write_stall_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_stall{instance=~\"$instance\", db=\"$db\", type=\"write_stall_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_stall{instance=~\"$instance\", db=\"$db\", type=\"write_stall_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Write stall duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 10, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 103, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_memory_bytes{instance=~\"$instance\", db=\"$db\", type=\"mem-tables\"}) by (cf)", + "intervalFactor": 2, + "legendFormat": "{{cf}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memtable size", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 0, + "id": 88, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": null, + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_memtable_efficiency{instance=~\"$instance\", db=\"$db\", type=\"memtable_hit\"}[1m])) / (sum(rate(tikv_engine_memtable_efficiency{db=\"$db\", type=\"memtable_hit\"}[1m])) + sum(rate(tikv_engine_memtable_efficiency{db=\"$db\", type=\"memtable_miss\"}[1m])))", + "intervalFactor": 2, + "legendFormat": "hit", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memtable hit", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 102, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_block_cache_size_bytes{instance=~\"$instance\", db=\"$db\"}) by(cf)", + "intervalFactor": 2, + "legendFormat": "{{cf}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Block cache size", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 0, + "id": 80, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 6, + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_cache_efficiency{instance=~\"$instance\", db=\"$db\", type=\"block_cache_hit\"}[1m])) / (sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_hit\"}[1m])) + sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_miss\"}[1m])))", + "intervalFactor": 2, + "legendFormat": "all", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{instance=~\"$instance\", db=\"$db\", type=\"block_cache_data_hit\"}[1m])) / (sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_data_hit\"}[1m])) + sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_data_miss\"}[1m])))", + "intervalFactor": 2, + "legendFormat": "data", + "metric": "", + "refId": "D", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{instance=~\"$instance\", db=\"$db\", type=\"block_cache_filter_hit\"}[1m])) / (sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_filter_hit\"}[1m])) + sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_filter_miss\"}[1m])))", + "intervalFactor": 2, + "legendFormat": "filter", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{instance=~\"$instance\", db=\"$db\", type=\"block_cache_index_hit\"}[1m])) / (sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_index_hit\"}[1m])) + sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_index_miss\"}[1m])))", + "intervalFactor": 2, + "legendFormat": "index", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_bloom_efficiency{instance=~\"$instance\", db=\"$db\", type=\"bloom_prefix_useful\"}[1m])) / sum(rate(tikv_engine_bloom_efficiency{db=\"$db\", type=\"bloom_prefix_checked\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "bloom prefix", + "metric": "", + "refId": "E", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Block cache hit", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 0, + "height": "", + "id": 467, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_flow_bytes{instance=~\"$instance\", db=\"$db\", type=\"block_cache_byte_read\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "total_read", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{instance=~\"$instance\", db=\"$db\", type=\"block_cache_byte_write\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "total_written", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{instance=~\"$instance\", db=\"$db\", type=\"block_cache_data_bytes_insert\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "data_insert", + "metric": "", + "refId": "D", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{instance=~\"$instance\", db=\"$db\", type=\"block_cache_filter_bytes_insert\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "filter_insert", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{instance=~\"$instance\", db=\"$db\", type=\"block_cache_filter_bytes_evict\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "filter_evict", + "metric": "", + "refId": "E", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{instance=~\"$instance\", db=\"$db\", type=\"block_cache_index_bytes_insert\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "index_insert", + "metric": "", + "refId": "F", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{instance=~\"$instance\", db=\"$db\", type=\"block_cache_index_bytes_evict\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "index_evict", + "metric": "", + "refId": "G", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Block cache flow", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 468, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_cache_efficiency{instance=~\"$instance\", db=\"$db\", type=\"block_cache_add\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "total_add", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{instance=~\"$instance\", db=\"$db\", type=\"block_cache_data_add\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "data_add", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{instance=~\"$instance\", db=\"$db\", type=\"block_cache_filter_add\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "filter_add", + "metric": "", + "refId": "D", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{instance=~\"$instance\", db=\"$db\", type=\"block_cache_index_add\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "index_add", + "metric": "", + "refId": "E", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{instance=~\"$instance\", db=\"$db\", type=\"block_cache_add_failures\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "add_failures", + "metric": "", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Block cache operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 0, + "height": "", + "id": 132, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_flow_bytes{instance=~\"$instance\", db=\"$db\", type=\"keys_read\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "read", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{instance=~\"$instance\", db=\"$db\", type=\"keys_written\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "written", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_compaction_num_corrupt_keys{instance=~\"$instance\", db=\"$db\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "corrupt", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Keys flow", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 131, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_estimate_num_keys{instance=~\"$instance\", db=\"$db\"}) by (cf)", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{cf}}", + "metric": "tikv_engine_estimate_num_keys", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Total keys", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 0, + "height": "", + "id": 85, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_flow_bytes{instance=~\"$instance\", db=\"$db\", type=\"bytes_read\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "get", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{instance=~\"$instance\", db=\"$db\", type=\"iter_bytes_read\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "scan", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Read flow", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 0, + "id": 133, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 6, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_bytes_per_read{instance=~\"$instance\", db=\"$db\",type=\"bytes_per_read_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_read{instance=~\"$instance\", db=\"$db\",type=\"bytes_per_read_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_read{instance=~\"$instance\", db=\"$db\",type=\"bytes_per_read_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_read{instance=~\"$instance\", db=\"$db\",type=\"bytes_per_read_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Bytes / Read", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "height": "", + "id": 86, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_flow_bytes{instance=~\"$instance\", db=\"$db\", type=\"wal_file_bytes\"}[1m]))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "wal", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{instance=~\"$instance\", db=\"$db\", type=\"bytes_written\"}[1m]))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "write", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Write flow", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 0, + "id": 134, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 6, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_bytes_per_write{instance=~\"$instance\", db=\"$db\",type=\"bytes_per_write_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_write{instance=~\"$instance\", db=\"$db\",type=\"bytes_per_write_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_write{instance=~\"$instance\", db=\"$db\",type=\"bytes_per_write_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_write{instance=~\"$instance\", db=\"$db\",type=\"bytes_per_write_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Bytes / Write", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 90, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_compaction_flow_bytes{instance=~\"$instance\", db=\"$db\", type=\"bytes_read\"}[1m]))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "read", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_compaction_flow_bytes{instance=~\"$instance\", db=\"$db\", type=\"bytes_written\"}[1m]))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "written", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{instance=~\"$instance\", db=\"$db\", type=\"flush_write_bytes\"}[1m]))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "flushed", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compaction flow", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 127, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_pending_compaction_bytes{instance=~\"$instance\", db=\"$db\"}[1m])) by (cf)", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{cf}}", + "metric": "tikv_engine_pending_compaction_bytes", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compaction pending bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 518, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_read_amp_flow_bytes{instance=~\"$instance\", db=\"$db\", type=\"read_amp_total_read_bytes\"}[1m])) by (instance) / sum(rate(tikv_engine_read_amp_flow_bytes{db=\"$db\", type=\"read_amp_estimate_useful_bytes\"}[1m])) by (instance)", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Read amplication", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 863, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_compression_ratio{instance=~\"$instance\", db=\"$db\"}) by (level)", + "hide": false, + "intervalFactor": 2, + "legendFormat": "level - {{level}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compression ratio", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 516, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "tikv_engine_num_snapshots{instance=~\"$instance\", db=\"$db\"}", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Number of snapshots", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 517, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "tikv_engine_oldest_snapshot_duration{instance=~\"$instance\", db=\"$db\"}", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tikv_engine_oldest_snapshot_duration", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Oldest snapshots duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 2002, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": true, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_num_files_at_level{instance=~\"$instance\", db=\"$db\"}) by (cf, level)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "cf-{{cf}}, level-{{level}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Number files at each level", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 2003, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": true, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_snapshot_ingest_sst_duration_seconds_bucket{instance=~\"$instance\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A" + }, + { + "expr": "sum(rate(tikv_snapshot_ingest_sst_duration_seconds_sum{instance=~\"$instance\"}[1m])) / sum(rate(tikv_snapshot_ingest_sst_duration_seconds_count{instance=~\"$instance\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "average", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Ingest SST duration seconds", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "fill": 1, + "id": 2381, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideZero": true, + "max": true, + "min": true, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "tikv_engine_stall_conditions_changed{instance=~\"$instance\", db=\"$db\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{cf}}-{{type}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Stall conditions changed of each CF", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 2451, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_compaction_reason{instance=~\"$instance\", db=\"$db\"}[1m])) by (cf, reason)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{cf}} - {{reason}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compression reason", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + } + ], + "repeat": "db", + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "RocksDB - $db", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 95, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_duration_seconds_count{instance=~\"$instance\", type!=\"kv_gc\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_grpc_msg_duration_seconds_bucket", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "gRPC message count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 107, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_fail_total{instance=~\"$instance\", type!=\"kv_gc\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_grpc_msg_fail_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "gRPC message failed", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 98, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": false, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_grpc_msg_duration_seconds_bucket{instance=~\"$instance\", type!=\"kv_gc\"}[1m])) by (le, type))", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% gRPC messge duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 10, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 1844, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_duration_seconds_count{instance=~\"$instance\", type=\"kv_gc\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "gc message cnt", + "metric": "tikv_grpc_msg_duration_seconds_bucket", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_grpc_msg_fail_total{instance=~\"$instance\", type=\"kv_gc\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "gc failed cnt", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "gRPC GC message count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 1845, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_grpc_msg_duration_seconds_bucket{instance=~\"$instance\", type=\"kv_gc\"}[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% gRPC KV GC messge duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 10, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "gRPC", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 1069, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 350, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_pd_request_duration_seconds_count{instance=~\"$instance\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{ type }}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 1070, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 350, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_pd_request_duration_seconds_sum{instance=~\"$instance\"}[1m])) by (type) / sum(rate(tikv_pd_request_duration_seconds_count{instance=~\"$instance\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{ type }}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD request duration (average)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 1215, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 350, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_pd_heartbeat_message_total{instance=~\"$instance\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{ type }}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD heartbeats", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "tidb-cluster", + "decimals": 1, + "fill": 1, + "id": 1396, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 350, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_pd_validate_peer_total{instance=~\"$instance\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{ type }}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD validate peers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "PD", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "tidb-cluster", + "hide": 0, + "includeAll": true, + "label": "db", + "multi": true, + "name": "db", + "options": [], + "query": "label_values(tikv_engine_block_cache_size_bytes, db)", + "refresh": 1, + "regex": "", + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "tidb-cluster", + "hide": 0, + "includeAll": true, + "label": "command", + "multi": true, + "name": "command", + "options": [], + "query": "label_values(tikv_storage_command_total, type)", + "refresh": 1, + "regex": "", + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": {}, + "datasource": "tidb-cluster", + "hide": 0, + "includeAll": true, + "label": "Instance", + "multi": false, + "name": "instance", + "options": [], + "query": "label_values(tikv_engine_size_bytes, instance)", + "refresh": 1, + "regex": "", + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "TIDB-Cluster-TiKV", + "version": 2 +} diff --git a/docker/tidb/config/drainer.toml b/docker/tidb/config/drainer.toml new file mode 100755 index 0000000..b2190e3 --- /dev/null +++ b/docker/tidb/config/drainer.toml @@ -0,0 +1,101 @@ +# drainer Configuration. + + # addr (i.e. 'host:port') to listen on for drainer connections +# will register this addr into etcd +# addr = "127.0.0.1:8249" + + # the interval time (in seconds) of detect pumps' status +detect-interval = 10 + + # drainer meta data directory path +data-dir = "data.drainer" + + # a comma separated list of PD endpoints +pd-urls = "http://127.0.0.1:2379" + + # Use the specified compressor to compress payload between pump and drainer +compressor = "" + + #[security] +# Path of file that contains list of trusted SSL CAs for connection with cluster components. +# ssl-ca = "/path/to/ca.pem" +# Path of file that contains X509 certificate in PEM format for connection with cluster components. +# ssl-cert = "/path/to/pump.pem" +# Path of file that contains X509 key in PEM format for connection with cluster components. +# ssl-key = "/path/to/pump-key.pem" + + # syncer Configuration. +[syncer] + + # Assume the upstream sql-mode. +# If this is setted , will use the same sql-mode to parse DDL statment, and set the same sql-mode at downstream when db-type is mysql. +# If this is not setted, it will not set any sql-mode. +# sql-mode = "STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION" + + # number of binlog events in a transaction batch +txn-batch = 20 + + # work count to execute binlogs +# if the latency between drainer and downstream(mysql or tidb) are too high, you might want to increase this +# to get higher throughput by higher concurrent write to the downstream +worker-count = 16 + + disable-dispatch = false + + # safe mode will split update to delete and insert +safe-mode = false + + # downstream storage, equal to --dest-db-type +# valid values are "mysql", "file", "tidb", "flash", "kafka" +db-type = "kafka" + + # disable sync these schema +ignore-schemas = "INFORMATION_SCHEMA,PERFORMANCE_SCHEMA,mysql" + + ##replicate-do-db priority over replicate-do-table if have same db name +##and we support regex expression , start with '~' declare use regex expression. +# +#replicate-do-db = ["~^b.*","s1"] + + #[[syncer.replicate-do-table]] +#db-name ="test" +#tbl-name = "log" + + #[[syncer.replicate-do-table]] +#db-name ="test" +#tbl-name = "~^a.*" + + # disable sync these table +#[[syncer.ignore-table]] +#db-name = "test" +#tbl-name = "log" + + # the downstream mysql protocol database +#[syncer.to] +#host = "127.0.0.1" +#user = "root" +#password = "" +#port = 3306 + + [syncer.to.checkpoint] +# you can uncomment this to change the database to save checkpoint when the downstream is mysql or tidb +#schema = "tidb_binlog" + + # Uncomment this if you want to use file as db-type. +#[syncer.to] +# directory to save binlog file, default same as data-dir(save checkpoint file) if this is not configured. +# dir = "data.drainer" + + + # when db-type is kafka, you can uncomment this to config the down stream kafka, it will be the globle config kafka default +[syncer.to] +# only need config one of zookeeper-addrs and kafka-addrs, will get kafka address if zookeeper-addrs is configed. +# zookeeper-addrs = "127.0.0.1:2181" + kafka-addrs = "kafka0:9092,kafka1:9093,kafka2:9094" + kafka-version = "2.1.1" + kafka-max-messages = 1024 +# +# +# the topic name drainer will push msg, the default name is _obinlog +# be careful don't use the same name if run multi drainer instances +# topic-name = "" diff --git a/docker/tidb/config/grafana-datasource.json b/docker/tidb/config/grafana-datasource.json new file mode 100755 index 0000000..c5581d6 --- /dev/null +++ b/docker/tidb/config/grafana-datasource.json @@ -0,0 +1,7 @@ +{ + "name": "tidb-cluster", + "type": "prometheus", + "url": "http://prometheus:9090", + "access": "proxy", + "basicAuth": false +} diff --git a/docker/tidb/config/grafana/grafana.ini b/docker/tidb/config/grafana/grafana.ini new file mode 100755 index 0000000..3a639fc --- /dev/null +++ b/docker/tidb/config/grafana/grafana.ini @@ -0,0 +1,556 @@ +##################### Grafana Configuration Defaults ##################### +# +# Do not modify this file in grafana installs +# + +# possible values : production, development +app_mode = production + +# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty +instance_name = ${HOSTNAME} + +#################################### Paths ############################### +[paths] +# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used) +data = data + +# Temporary files in `data` directory older than given duration will be removed +temp_data_lifetime = 24h + +# Directory where grafana can store logs +logs = data/log + +# Directory where grafana will automatically scan and look for plugins +plugins = data/plugins + +# folder that contains provisioning config files that grafana will apply on startup and while running. +provisioning = conf/provisioning + +#################################### Server ############################## +[server] +# Protocol (http, https, socket) +protocol = http + +# The ip address to bind to, empty will bind to all interfaces +http_addr = + +# The http port to use +http_port = 3000 + +# The public facing domain name used to access grafana from a browser +domain = localhost + +# Redirect to correct domain if host header does not match domain +# Prevents DNS rebinding attacks +enforce_domain = false + +# The full public facing url +root_url = %(protocol)s://%(domain)s:%(http_port)s/ + +# Log web requests +router_logging = false + +# the path relative working path +static_root_path = public + +# enable gzip +enable_gzip = false + +# https certs & key file +cert_file = +cert_key = + +# Unix socket path +socket = /tmp/grafana.sock + +#################################### Database ############################ +[database] +# You can configure the database connection by specifying type, host, name, user and password +# as separate properties or as on string using the url property. + +# Either "mysql", "postgres" or "sqlite3", it's your choice +type = sqlite3 +host = 127.0.0.1:3306 +name = grafana +user = root +# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;""" +password = +# Use either URL or the previous fields to configure the database +# Example: mysql://user:secret@host:port/database +url = + +# Max idle conn setting default is 2 +max_idle_conn = 2 + +# Max conn setting default is 0 (mean not set) +max_open_conn = + +# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours) +conn_max_lifetime = 14400 + +# Set to true to log the sql calls and execution times. +log_queries = + +# For "postgres", use either "disable", "require" or "verify-full" +# For "mysql", use either "true", "false", or "skip-verify". +ssl_mode = disable + +ca_cert_path = +client_key_path = +client_cert_path = +server_cert_name = + +# For "sqlite3" only, path relative to data_path setting +path = grafana.db + +#################################### Session ############################# +[session] +# Either "memory", "file", "redis", "mysql", "postgres", "memcache", default is "file" +provider = file + +# Provider config options +# memory: not have any config yet +# file: session dir path, is relative to grafana data_path +# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=grafana` +# postgres: user=a password=b host=localhost port=5432 dbname=c sslmode=disable +# mysql: go-sql-driver/mysql dsn config string, examples: +# `user:password@tcp(127.0.0.1:3306)/database_name` +# `user:password@unix(/var/run/mysqld/mysqld.sock)/database_name` +# memcache: 127.0.0.1:11211 + + +provider_config = sessions + +# Session cookie name +cookie_name = grafana_sess + +# If you use session in https only, default is false +cookie_secure = false + +# Session life time, default is 86400 +session_life_time = 86400 +gc_interval_time = 86400 + +# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours) +conn_max_lifetime = 14400 + +#################################### Data proxy ########################### +[dataproxy] + +# This enables data proxy logging, default is false +logging = false + +#################################### Analytics ########################### +[analytics] +# Server reporting, sends usage counters to stats.grafana.org every 24 hours. +# No ip addresses are being tracked, only simple counters to track +# running instances, dashboard and error counts. It is very helpful to us. +# Change this option to false to disable reporting. +reporting_enabled = true + +# Set to false to disable all checks to https://grafana.com +# for new versions (grafana itself and plugins), check is used +# in some UI views to notify that grafana or plugin update exists +# This option does not cause any auto updates, nor send any information +# only a GET request to https://grafana.com to get latest versions +check_for_updates = true + +# Google Analytics universal tracking code, only enabled if you specify an id here +google_analytics_ua_id = + +# Google Tag Manager ID, only enabled if you specify an id here +google_tag_manager_id = + +#################################### Security ############################ +[security] +# default admin user, created on startup +admin_user = admin + +# default admin password, can be changed before first start of grafana, or in profile settings +admin_password = admin + +# used for signing +secret_key = SW2YcwTIb9zpOOhoPsMm + +# Auto-login remember days +login_remember_days = 7 +cookie_username = grafana_user +cookie_remember_name = grafana_remember + +# disable gravatar profile images +disable_gravatar = false + +# data source proxy whitelist (ip_or_domain:port separated by spaces) +data_source_proxy_whitelist = + +# disable protection against brute force login attempts +disable_brute_force_login_protection = false + +#################################### Snapshots ########################### +[snapshots] +# snapshot sharing options +external_enabled = true +external_snapshot_url = https://snapshots-origin.raintank.io +external_snapshot_name = Publish to snapshot.raintank.io + +# remove expired snapshot +snapshot_remove_expired = true + +#################################### Dashboards ################## + +[dashboards] +# Number dashboard versions to keep (per dashboard). Default: 20, Minimum: 1 +versions_to_keep = 20 + +#################################### Users ############################### +[users] +# disable user signup / registration +allow_sign_up = false + +# Allow non admin users to create organizations +allow_org_create = false + +# Set to true to automatically assign new users to the default organization (id 1) +auto_assign_org = true + +# Set this value to automatically add new users to the provided organization (if auto_assign_org above is set to true) +auto_assign_org_id = 1 + +# Default role new users will be automatically assigned (if auto_assign_org above is set to true) +auto_assign_org_role = Viewer + +# Require email validation before sign up completes +verify_email_enabled = false + +# Background text for the user field on the login page +login_hint = email or username + +# Default UI theme ("dark" or "light") +default_theme = dark + +# External user management +external_manage_link_url = +external_manage_link_name = +external_manage_info = + +# Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard. +viewers_can_edit = false + +[auth] +# Set to true to disable (hide) the login form, useful if you use OAuth +disable_login_form = false + +# Set to true to disable the signout link in the side menu. useful if you use auth.proxy +disable_signout_menu = false + +# URL to redirect the user to after sign out +signout_redirect_url = + +#################################### Anonymous Auth ###################### +[auth.anonymous] +# enable anonymous access +enabled = true + +# specify organization name that should be used for unauthenticated users +org_name = Main Org. + +# specify role for unauthenticated users +org_role = Viewer + +#################################### Github Auth ######################### +[auth.github] +enabled = false +allow_sign_up = true +client_id = some_id +client_secret = some_secret +scopes = user:email,read:org +auth_url = https://github.com/login/oauth/authorize +token_url = https://github.com/login/oauth/access_token +api_url = https://api.github.com/user +team_ids = +allowed_organizations = + +#################################### GitLab Auth ######################### +[auth.gitlab] +enabled = false +allow_sign_up = true +client_id = some_id +client_secret = some_secret +scopes = api +auth_url = https://gitlab.com/oauth/authorize +token_url = https://gitlab.com/oauth/token +api_url = https://gitlab.com/api/v4 +allowed_groups = + +#################################### Google Auth ######################### +[auth.google] +enabled = false +allow_sign_up = true +client_id = some_client_id +client_secret = some_client_secret +scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email +auth_url = https://accounts.google.com/o/oauth2/auth +token_url = https://accounts.google.com/o/oauth2/token +api_url = https://www.googleapis.com/oauth2/v1/userinfo +allowed_domains = +hosted_domain = + +#################################### Grafana.com Auth #################### +# legacy key names (so they work in env variables) +[auth.grafananet] +enabled = false +allow_sign_up = true +client_id = some_id +client_secret = some_secret +scopes = user:email +allowed_organizations = + +[auth.grafana_com] +enabled = false +allow_sign_up = true +client_id = some_id +client_secret = some_secret +scopes = user:email +allowed_organizations = + +#################################### Generic OAuth ####################### +[auth.generic_oauth] +name = OAuth +enabled = false +allow_sign_up = true +client_id = some_id +client_secret = some_secret +scopes = user:email +email_attribute_name = email:primary +auth_url = +token_url = +api_url = +team_ids = +allowed_organizations = +tls_skip_verify_insecure = false +tls_client_cert = +tls_client_key = +tls_client_ca = + +#################################### Basic Auth ########################## +[auth.basic] +enabled = true + +#################################### Auth Proxy ########################## +[auth.proxy] +enabled = false +header_name = X-WEBAUTH-USER +header_property = username +auto_sign_up = true +ldap_sync_ttl = 60 +whitelist = + +#################################### Auth LDAP ########################### +[auth.ldap] +enabled = false +config_file = /etc/grafana/ldap.toml +allow_sign_up = true + +#################################### SMTP / Emailing ##################### +[smtp] +enabled = false +host = localhost:25 +user = +# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;""" +password = +cert_file = +key_file = +skip_verify = false +from_address = admin@grafana.localhost +from_name = Grafana +ehlo_identity = + +[emails] +welcome_email_on_sign_up = false +templates_pattern = emails/*.html + +#################################### Logging ########################## +[log] +# Either "console", "file", "syslog". Default is console and file +# Use space to separate multiple modes, e.g. "console file" +mode = console file + +# Either "debug", "info", "warn", "error", "critical", default is "info" +level = info + +# optional settings to set different levels for specific loggers. Ex filters = sqlstore:debug +filters = + +# For "console" mode only +[log.console] +level = + +# log line format, valid options are text, console and json +format = console + +# For "file" mode only +[log.file] +level = + +# log line format, valid options are text, console and json +format = text + +# This enables automated log rotate(switch of following options), default is true +log_rotate = true + +# Max line number of single file, default is 1000000 +max_lines = 1000000 + +# Max size shift of single file, default is 28 means 1 << 28, 256MB +max_size_shift = 28 + +# Segment log daily, default is true +daily_rotate = true + +# Expired days of log file(delete after max days), default is 7 +max_days = 7 + +[log.syslog] +level = + +# log line format, valid options are text, console and json +format = text + +# Syslog network type and address. This can be udp, tcp, or unix. If left blank, the default unix endpoints will be used. +network = +address = + +# Syslog facility. user, daemon and local0 through local7 are valid. +facility = + +# Syslog tag. By default, the process' argv[0] is used. +tag = + +#################################### Usage Quotas ######################## +[quota] +enabled = false + +#### set quotas to -1 to make unlimited. #### +# limit number of users per Org. +org_user = 10 + +# limit number of dashboards per Org. +org_dashboard = 100 + +# limit number of data_sources per Org. +org_data_source = 10 + +# limit number of api_keys per Org. +org_api_key = 10 + +# limit number of orgs a user can create. +user_org = 10 + +# Global limit of users. +global_user = -1 + +# global limit of orgs. +global_org = -1 + +# global limit of dashboards +global_dashboard = -1 + +# global limit of api_keys +global_api_key = -1 + +# global limit on number of logged in users. +global_session = -1 + +#################################### Alerting ############################ +[alerting] +# Disable alerting engine & UI features +enabled = true +# Makes it possible to turn off alert rule execution but alerting UI is visible +execute_alerts = true + +# Default setting for new alert rules. Defaults to categorize error and timeouts as alerting. (alerting, keep_state) +error_or_timeout = alerting + +# Default setting for how Grafana handles nodata or null values in alerting. (alerting, no_data, keep_state, ok) +nodata_or_nullvalues = no_data + +# Alert notifications can include images, but rendering many images at the same time can overload the server +# This limit will protect the server from render overloading and make sure notifications are sent out quickly +concurrent_render_limit = 5 + +#################################### Explore ############################# +[explore] +# Enable the Explore section +enabled = false + +#################################### Internal Grafana Metrics ############ +# Metrics available at HTTP API Url /metrics +[metrics] +enabled = true +interval_seconds = 10 + +# Send internal Grafana metrics to graphite +[metrics.graphite] +# Enable by setting the address setting (ex localhost:2003) +address = +prefix = prod.grafana.%(instance_name)s. + +[grafana_net] +url = https://grafana.com + +[grafana_com] +url = https://grafana.com + +#################################### Distributed tracing ############ +[tracing.jaeger] +# jaeger destination (ex localhost:6831) +address = +# tag that will always be included in when creating new spans. ex (tag1:value1,tag2:value2) +always_included_tag = +# Type specifies the type of the sampler: const, probabilistic, rateLimiting, or remote +sampler_type = const +# jaeger samplerconfig param +# for "const" sampler, 0 or 1 for always false/true respectively +# for "probabilistic" sampler, a probability between 0 and 1 +# for "rateLimiting" sampler, the number of spans per second +# for "remote" sampler, param is the same as for "probabilistic" +# and indicates the initial sampling rate before the actual one +# is received from the mothership +sampler_param = 1 + +#################################### External Image Storage ############## +[external_image_storage] +# You can choose between (s3, webdav, gcs, azure_blob, local) +provider = + +[external_image_storage.s3] +bucket_url = +bucket = +region = +path = +access_key = +secret_key = + +[external_image_storage.webdav] +url = +username = +password = +public_url = + +[external_image_storage.gcs] +key_file = +bucket = +path = + +[external_image_storage.azure_blob] +account_name = +account_key = +container_name = + +[external_image_storage.local] +# does not require any configuration + +[rendering] +# Options to configure external image rendering server like https://github.com/grafana/grafana-image-renderer +server_url = +callback_url = diff --git a/docker/tidb/config/grafana/provisioning/dashboards/dashboards.yaml b/docker/tidb/config/grafana/provisioning/dashboards/dashboards.yaml new file mode 100755 index 0000000..e2aa179 --- /dev/null +++ b/docker/tidb/config/grafana/provisioning/dashboards/dashboards.yaml @@ -0,0 +1,10 @@ +# # config file version +apiVersion: 1 + +providers: +- name: 'default' + orgId: 1 + folder: '' + type: file + options: + path: /tmp/dashboards diff --git a/docker/tidb/config/grafana/provisioning/datasources/datasources.yaml b/docker/tidb/config/grafana/provisioning/datasources/datasources.yaml new file mode 100755 index 0000000..7092ca5 --- /dev/null +++ b/docker/tidb/config/grafana/provisioning/datasources/datasources.yaml @@ -0,0 +1,53 @@ +# # config file version +apiVersion: 1 + +# # list of datasources that should be deleted from the database +#deleteDatasources: +# - name: Graphite +# orgId: 1 + +# # list of datasources to insert/update depending +# # on what's available in the datbase +datasources: + # name of the datasource. Required +- name: tidb-cluster + # datasource type. Required + type: prometheus + # access mode. direct or proxy. Required + access: proxy +# # org id. will default to orgId 1 if not specified +# orgId: 1 + # url + url: http://prometheus:9090 +# # database password, if used +# password: +# # database user, if used +# user: +# # database name, if used +# database: + # enable/disable basic auth + basicAuth: false +# # basic auth username +# basicAuthUser: +# # basic auth password +# basicAuthPassword: +# # enable/disable with credentials headers +# withCredentials: +# # mark as default datasource. Max one per org +# isDefault: +# # fields that will be converted to json and stored in json_data +# jsonData: +# graphiteVersion: "1.1" +# tlsAuth: true +# tlsAuthWithCACert: true +# httpHeaderName1: "Authorization" +# # json object of data that will be encrypted. +# secureJsonData: +# tlsCACert: "..." +# tlsClientCert: "..." +# tlsClientKey: "..." +# # +# httpHeaderValue1: "Bearer xf5yhfkpsnmgo" +# version: 1 +# # allow users to edit datasources from the UI. +# editable: false diff --git a/docker/tidb/config/overview-dashboard.json b/docker/tidb/config/overview-dashboard.json new file mode 100755 index 0000000..6d54dfd --- /dev/null +++ b/docker/tidb/config/overview-dashboard.json @@ -0,0 +1,4892 @@ +{ + "__inputs": [ + { + "name": "DS_TEST-CLUSTER", + "label": "test-cluster", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "4.6.3" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "singlestat", + "name": "Singlestat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "${DS_TEST-CLUSTER}", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "hideControls": false, + "id": null, + "links": [ + { + "icon": "doc", + "includeVars": true, + "keepTime": true, + "tags": [], + "targetBlank": true, + "title": "Report", + "tooltip": "Open a pdf report for the current dashboard", + "type": "link", + "url": "http://172.16.10.71:8686/api/report/test-cluster-overview?apitoken=eyJrIjoiWTlFZ0M0REpKbFY3aFRvOVNJZEh6cWNJaTkxY3NLdTYiLCJuIjoiZ3JhZmFuYV9hcGlrZXkiLCJpZCI6MX0=" + } + ], + "refresh": "1m", + "rows": [ + { + "collapse": false, + "height": 310, + "panels": [ + { + "columns": [ + { + "text": "Current", + "value": "current" + } + ], + "datasource": "${DS_TEST-CLUSTER}", + "fontSize": "100%", + "hideTimeOverride": true, + "id": 76, + "links": [], + "pageSize": null, + "scroll": true, + "showHeader": true, + "sort": { + "col": null, + "desc": false + }, + "span": 6, + "styles": [ + { + "alias": "Time", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "alias": "Service", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "pattern": "Metric", + "thresholds": [], + "type": "string", + "unit": "short" + }, + { + "alias": "Up", + "colorMode": "cell", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 0, + "pattern": "Current", + "thresholds": [ + "0", + "1" + ], + "type": "number", + "unit": "short" + }, + { + "alias": "", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "expr": "\ncount(probe_success{group=\"tidb\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "TiDB", + "refId": "A" + }, + { + "expr": "\ncount(probe_success{group=\"pd\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "PD", + "refId": "B" + }, + { + "expr": "\ncount(probe_success{group=\"tikv\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "TiKV", + "refId": "C" + }, + { + "expr": "\ncount(probe_success{group=\"pump\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Pump", + "refId": "D" + }, + { + "expr": "\ncount(probe_success{group=\"drainer\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Drainer", + "refId": "E" + }, + { + "expr": "\ncount(probe_success{group=\"kafka\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Kafka", + "refId": "F" + }, + { + "expr": "\ncount(probe_success{group=\"zookeeper\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Zookeeper", + "refId": "G" + }, + { + "expr": "\ncount(probe_success{group=\"node_exporter\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Node_exporter", + "refId": "H" + }, + { + "expr": "\ncount(probe_success{group=\"blackbox_exporter\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Blackbox_exporter", + "refId": "I" + }, + { + "expr": "\ncount(probe_success{group=\"grafana\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Grafana", + "refId": "J" + }, + { + "expr": "\ncount(probe_success{job=\"blackbox_exporter_http\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Pushgateway", + "refId": "K" + }, + { + "expr": "\ncount(probe_success{group=\"kafka_exporter\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Kafka_exporter", + "refId": "L" + } + ], + "timeFrom": "1s", + "title": "", + "transform": "timeseries_aggregations", + "type": "table" + }, + { + "columns": [ + { + "text": "Current", + "value": "current" + } + ], + "datasource": "${DS_TEST-CLUSTER}", + "fontSize": "100%", + "hideTimeOverride": true, + "id": 77, + "links": [], + "pageSize": null, + "scroll": true, + "showHeader": true, + "sort": { + "col": null, + "desc": false + }, + "span": 6, + "styles": [ + { + "alias": "Time", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "alias": "Service", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "pattern": "Metric", + "thresholds": [], + "type": "string", + "unit": "short" + }, + { + "alias": "Down", + "colorMode": "cell", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 0, + "pattern": "Current", + "thresholds": [ + "100", + "200" + ], + "type": "number", + "unit": "short" + }, + { + "alias": "", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "expr": "\ncount(probe_success{group=\"tidb\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "TiDB", + "refId": "A" + }, + { + "expr": "\ncount(probe_success{group=\"pd\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "PD", + "refId": "B" + }, + { + "expr": "\ncount(probe_success{group=\"tikv\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "TiKV", + "refId": "C" + }, + { + "expr": "\ncount(probe_success{group=\"pump\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Pump", + "refId": "D" + }, + { + "expr": "\ncount(probe_success{group=\"drainer\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Drainer", + "refId": "E" + }, + { + "expr": "\ncount(probe_success{group=\"kafka\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Kafka", + "refId": "F" + }, + { + "expr": "\ncount(probe_success{group=\"zookeeper\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Zookeeper", + "refId": "G" + }, + { + "expr": "\ncount(probe_success{group=\"node_exporter\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Node_exporter", + "refId": "H" + }, + { + "expr": "\ncount(probe_success{group=\"blackbox_exporter\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Blackbox_exporter", + "refId": "I" + }, + { + "expr": "\ncount(probe_success{group=\"grafana\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Grafana", + "refId": "J" + }, + { + "expr": "\ncount(probe_success{job=\"blackbox_exporter_http\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Pushgateway", + "refId": "K" + }, + { + "expr": "\ncount(probe_success{group=\"kafka_exporter\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Kafka_exporter", + "refId": "L" + } + ], + "timeFrom": "1s", + "title": "", + "transform": "timeseries_aggregations", + "type": "table" + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Services Port Status", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 29, + "interval": null, + "links": [], + "mappingType": 2, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "1", + "text": "Leader", + "to": "100000" + }, + { + "from": "0", + "text": "Follower", + "to": "1" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "delta(pd_server_tso{type=\"save\",instance=\"$instance\"}[1m])", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "", + "metric": "pd_server_tso", + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "PD Role", + "type": "singlestat", + "valueFontSize": "50%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "1" + }, + { + "op": "=", + "text": "", + "value": "" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "format": "bytes", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": false + }, + "id": 27, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(77, 135, 25, 0.18)", + "full": true, + "lineColor": "rgb(21, 179, 65)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"storage_capacity\"}", + "intervalFactor": 2, + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "Storage Capacity", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "format": "bytes", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "hideTimeOverride": false, + "id": 28, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"storage_size\"}", + "intervalFactor": 2, + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "Current Storage Size", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 30, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"region_count\"}", + "intervalFactor": 2, + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "Number of Regions", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "${DS_TEST-CLUSTER}", + "format": "percentunit", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 64, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 1, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "1 - min(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_score\"}) / max(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_score\"})", + "format": "time_series", + "interval": "15s", + "intervalFactor": 2, + "refId": "A" + } + ], + "thresholds": "0.01,0.5", + "title": "Leader Balance Ratio", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "${DS_TEST-CLUSTER}", + "format": "percentunit", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 65, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 1, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "1 - min(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_score\"}) / max(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_score\"})", + "format": "time_series", + "interval": "15s", + "intervalFactor": 2, + "refId": "A" + } + ], + "thresholds": "0.05,0.5", + "title": "Region Balance Ratio", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "columns": [ + { + "text": "Current", + "value": "current" + } + ], + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fontSize": "100%", + "hideTimeOverride": false, + "id": 18, + "links": [], + "pageSize": null, + "repeat": null, + "scroll": false, + "showHeader": true, + "sort": { + "col": null, + "desc": false + }, + "span": 2, + "styles": [ + { + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Metric", + "sanitize": false, + "type": "string" + }, + { + "colorMode": "cell", + "colors": [ + "rgba(50, 172, 45, 0.97)", + "rgba(237, 129, 40, 0.89)", + "rgba(245, 54, 54, 0.9)" + ], + "decimals": 0, + "pattern": "Current", + "thresholds": [ + "1", + "2" + ], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_up_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Up Stores", + "metric": "pd_cluster_status", + "refId": "A", + "step": 30 + }, + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_disconnected_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Disconnect Stores", + "metric": "pd_cluster_status", + "refId": "B", + "step": 30 + }, + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_low_space_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "LowSpace Stores", + "metric": "pd_cluster_status", + "refId": "C", + "step": 30 + }, + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_down_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Down Stores", + "metric": "pd_cluster_status", + "refId": "D", + "step": 30 + }, + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_offline_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Offline Stores", + "metric": "pd_cluster_status", + "refId": "E", + "step": 30 + }, + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_tombstone_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Tombstone Stores", + "metric": "pd_cluster_status", + "refId": "F", + "step": 30 + } + ], + "title": "Store Status", + "transform": "timeseries_aggregations", + "transparent": false, + "type": "table" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 24, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(grpc_server_handling_seconds_bucket{instance=\"$instance\"}[5m])) by (grpc_method, le))", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{grpc_method}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% completed_cmds_duration_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 32, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.98, sum(rate(pd_client_request_handle_requests_duration_seconds_bucket[30s])) by (type, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}} 98th percentile", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(rate(pd_client_request_handle_requests_duration_seconds_sum[30s])) by (type) / avg(rate(pd_client_request_handle_requests_duration_seconds_count[30s])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}} average", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "handle_requests_duration_seconds", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "s", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 66, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_regions_status{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + }, + { + "expr": "sum(pd_regions_status) by (instance, type)", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region Health", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 68, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_write_region_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot write region's leader distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 0, + "fill": 0, + "id": 69, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_read_region_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot read region's leader distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 33, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_region_heartbeat{instance=\"$instance\", type=\"report\", status=\"ok\"}[1m])) by (store)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region heartbeat report", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 67, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(pd_scheduler_region_heartbeat_latency_seconds_bucket[5m])) by (store, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% region heartbeat latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": "instance", + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "PD", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 2, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_executor_statement_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Statement OPS", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 34, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "hideEmpty": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 3, + "legendFormat": "99", + "refId": "B", + "step": 15 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "95", + "refId": "C" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 35, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(tidb_server_query_total[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} {{type}} {{result}}", + "refId": "A", + "step": 20 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "QPS By Instance", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 72, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_server_execute_error_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": " {{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Failed Query OPM", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 4, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "fill": 0, + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": true, + "targets": [ + { + "expr": "tidb_server_connections", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(tidb_server_connections)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "total", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Connection Count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 36, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "go_memstats_heap_inuse_bytes{job=~\"tidb.*\"}", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{job}}", + "metric": "go_memstats_heap_inuse_bytes", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Heap Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 70, + "legend": { + "avg": false, + "current": true, + "max": true, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_session_transaction_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Transaction OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 71, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "95", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Transaction Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 37, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_txn_cmd_duration_seconds_count[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Cmd OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 38, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_tikvclient_txn_cmd_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Cmd Duration 99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 39, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_client_cmd_handle_cmds_duration_seconds_count{type=\"tso\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "cmd", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(pd_client_request_handle_requests_duration_seconds_count{type=\"tso\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "request", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD TSO OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 40, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "hideEmpty": false, + "hideZero": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD TSO Wait Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "fill": 0, + "id": 41, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_region_err_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tidb_server_session_execute_parse_duration_count", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "TiClient Region Error OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 42, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_lock_resolver_actions_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tidb_tikvclient_lock_resolver_actions_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Lock Resolve OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 6, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": false, + "hideZero": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_domain_load_schema_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Load Schema Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 43, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": true, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_backoff_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Backoff OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "TiDB", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 299, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 20, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{type=\"leader\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 10 + }, + { + "expr": "delta(tikv_pd_heartbeat_tick_total{type=\"leader\"}[30s]) < -10", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "total", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "leader", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 21, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{type=\"region\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "region", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 75, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total[1m])) by (instance,job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{job}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "CPU", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 74, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(process_resident_memory_bytes{job=~\"tikv.*\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memory", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 5, + "id": 44, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 0, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_size_bytes) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "store size", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 73, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_size_bytes) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "cf size", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 3, + "grid": {}, + "id": 17, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_channel_full_total[1m])) by (job, type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}} - {{type}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "channel full", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 11, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_server_report_failure_msg_total[1m])) by (type,instance,job,store_id)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}} - {{type}} - to - {{store_id}}", + "metric": "tikv_server_raft_store_msg_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "server report failures", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 46, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_scheduler_contex_total) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler pending commands", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 45, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": false, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_pending_request[1m])) by (req, priority)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ req }} - {{priority}}", + "metric": "tikv_region_written_keys_bucket", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_coprocessor_pending_request[1m])) BY (type, instance)", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor pending requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 51, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_executor_count[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_coprocessor_request_error", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor executor count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 5, + "fill": 1, + "id": 47, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_coprocessor_request_duration_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-99%", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_duration_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-95%", + "refId": "B", + "step": 10 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_duration_seconds_sum{req=\"select\"}[1m])) / sum(rate(tikv_coprocessor_request_duration_seconds_count{req=\"select\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "select-avg", + "refId": "C", + "step": 10 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_duration_seconds_sum{req=\"index\"}[1m])) / sum(rate(tikv_coprocessor_request_duration_seconds_count{req=\"index\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "index-avg", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor request duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 48, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"raftstore_.*\"}[1m])) by (job, name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "raft store CPU", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 49, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"cop_.*\"}[1m])) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_coprocessor_request_duration_seconds_bucket", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Coprocessor CPU", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "TiKV", + "titleSize": "h6" + }, + { + "collapse": false, + "height": 250, + "panels": [ + { + "columns": [ + { + "text": "Current", + "value": "current" + } + ], + "datasource": "${DS_TEST-CLUSTER}", + "fontSize": "100%", + "hideTimeOverride": false, + "id": 57, + "links": [], + "pageSize": null, + "scroll": true, + "showHeader": true, + "sort": { + "col": null, + "desc": false + }, + "span": 3, + "styles": [ + { + "alias": "Host", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "link": false, + "pattern": "Metric", + "preserveFormat": false, + "sanitize": false, + "thresholds": [], + "type": "string", + "unit": "short" + }, + { + "alias": "CPU Num", + "colorMode": "value", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(30, 232, 22, 0.97)" + ], + "decimals": 0, + "link": false, + "pattern": "Current", + "thresholds": [ + "0", + "1" + ], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "expr": "count(node_cpu{mode=\"user\"}) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ instance }}", + "refId": "A", + "step": 2 + } + ], + "timeFrom": null, + "title": "Vcores", + "transform": "timeseries_aggregations", + "type": "table" + }, + { + "columns": [ + { + "text": "Current", + "value": "current" + } + ], + "datasource": "${DS_TEST-CLUSTER}", + "fontSize": "100%", + "hideTimeOverride": true, + "id": 59, + "links": [], + "pageSize": null, + "scroll": true, + "showHeader": true, + "sort": { + "col": null, + "desc": false + }, + "span": 3, + "styles": [ + { + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "sanitize": false, + "type": "hidden" + }, + { + "alias": "Host", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "pattern": "Metric", + "thresholds": [], + "type": "string", + "unit": "short" + }, + { + "alias": "Memory", + "colorMode": "value", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 0, + "pattern": "Current", + "thresholds": [ + "0", + "1" + ], + "type": "number", + "unit": "bytes" + } + ], + "targets": [ + { + "expr": "node_memory_MemTotal", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ instance }}", + "refId": "A", + "step": 2 + } + ], + "timeFrom": "1s", + "title": "Memory", + "transform": "timeseries_aggregations", + "type": "table" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 55, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "100 - avg by (instance) (irate(node_cpu{mode=\"idle\"}[1m]) ) * 100", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 78, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "node_load1", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Load [1m]", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 58, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "node_memory_MemAvailable", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ instance }}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memory Available", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 79, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "irate(node_network_receive_bytes{device!=\"lo\"}[5m]) * 8", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Inbound: {{instance}}", + "refId": "A" + }, + { + "expr": "irate(node_network_transmit_bytes{device!=\"lo\"}[5m]) * 8", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Outbound: {{instance}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Network Traffic", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 60, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "irate(node_netstat_TcpExt_TCPSynRetrans[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - TCPSynRetrans", + "refId": "A", + "step": 10 + }, + { + "expr": "irate(node_netstat_TcpExt_TCPSlowStartRetrans[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - TCPSlowStartRetrans", + "refId": "B", + "step": 10 + }, + { + "expr": "irate(node_netstat_TcpExt_TCPForwardRetrans[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - TCPForwardRetrans", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "TCP Retrans", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 0, + "fill": 1, + "id": 61, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(node_disk_io_time_ms[1m]) / 1000", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - {{device}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "IO Util", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "System Info", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "${DS_TEST-CLUSTER}", + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "instance", + "options": [], + "query": "label_values(pd_cluster_status, instance)", + "refresh": 1, + "regex": "", + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": {}, + "datasource": "${DS_TEST-CLUSTER}", + "hide": 0, + "includeAll": true, + "label": "Namespace", + "multi": false, + "name": "namespace", + "options": [], + "query": "label_values(pd_cluster_status{instance=\"$instance\"}, namespace)", + "refresh": 1, + "regex": "", + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "Test-Cluster-Overview", + "version": 6 +} \ No newline at end of file diff --git a/docker/tidb/config/pd-dashboard.json b/docker/tidb/config/pd-dashboard.json new file mode 100755 index 0000000..c61f8f4 --- /dev/null +++ b/docker/tidb/config/pd-dashboard.json @@ -0,0 +1,5776 @@ +{ + "__inputs": [ + { + "name": "DS_TEST-CLUSTER", + "label": "test-cluster", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "4.6.3" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "singlestat", + "name": "Singlestat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "${DS_TEST-CLUSTER}", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 1, + "hideControls": false, + "id": null, + "links": [ + { + "icon": "doc", + "includeVars": true, + "keepTime": true, + "tags": [], + "targetBlank": true, + "title": "Report", + "tooltip": "Open a pdf report for the current dashboard", + "type": "link", + "url": "http://172.16.10.71:8686/api/report/test-cluster-pd?apitoken=eyJrIjoiNEg1WFRIbVBDVHhsclFaOEdSS2tVWDNveWNlM2RBcVkiLCJuIjoiZ3JhZmFuYV9hcGlrZXkiLCJpZCI6MX0=" + } + ], + "refresh": "30s", + "rows": [ + { + "collapse": false, + "height": 299, + "panels": [ + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 55, + "interval": null, + "links": [], + "mappingType": 2, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "1", + "text": "Leader", + "to": "100000" + }, + { + "from": "0", + "text": "Follower", + "to": "1" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "delta(pd_server_tso{type=\"save\",instance=\"$instance\"}[1m])", + "intervalFactor": 2, + "legendFormat": "", + "metric": "pd_server_tso", + "refId": "A", + "step": 40 + } + ], + "thresholds": "", + "title": "PD Role", + "type": "singlestat", + "valueFontSize": "50%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "format": "decbytes", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": false + }, + "id": 10, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(77, 135, 25, 0.18)", + "full": true, + "lineColor": "rgb(21, 179, 65)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(pd_cluster_status{instance=\"$instance\",namespace=~\"$namespace\",type=\"storage_capacity\"})", + "intervalFactor": 2, + "refId": "A", + "step": 40 + } + ], + "thresholds": "", + "title": "Storage Capacity", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "format": "decbytes", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "hideTimeOverride": false, + "id": 38, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(pd_cluster_status{instance=\"$instance\",namespace=~\"$namespace\",type=\"storage_size\"})", + "intervalFactor": 2, + "refId": "A", + "step": 40 + } + ], + "thresholds": "", + "title": "Current Storage Size", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": false + }, + "id": 20, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(pd_cluster_status{instance=\"$instance\",namespace=~\"$namespace\",type=\"leader_count\"})", + "format": "time_series", + "intervalFactor": 2, + "refId": "A", + "step": 40 + } + ], + "thresholds": "", + "title": "Number of Regions", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": [ + "rgba(50, 172, 45, 0.97)", + "rgba(237, 129, 40, 0.89)", + "rgba(245, 54, 54, 0.9)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "format": "percentunit", + "gauge": { + "maxValue": 1, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "hideTimeOverride": false, + "id": 37, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "1 - min(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_score\"}) / max(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_score\"})", + "format": "time_series", + "intervalFactor": 2, + "refId": "A", + "step": 40 + } + ], + "thresholds": "0.01,0.5", + "title": "Leader Balance Ratio", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": [ + "rgba(50, 172, 45, 0.97)", + "rgba(237, 129, 40, 0.89)", + "rgba(245, 54, 54, 0.9)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "format": "percentunit", + "gauge": { + "maxValue": 1, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 36, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "1 - min(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_score\"}) / max(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_score\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A", + "step": 40 + } + ], + "thresholds": "0.05,0.5", + "title": "Region Balance Ratio", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#d44a3a", + "rgba(237, 129, 40, 0.89)", + "#299c46" + ], + "datasource": "${DS_TEST-CLUSTER}", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 97, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 3, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_up_count\"})", + "format": "time_series", + "intervalFactor": 2, + "refId": "A" + } + ], + "thresholds": "0,1", + "title": "Normal Stores", + "type": "singlestat", + "valueFontSize": "100%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "columns": [ + { + "text": "Current", + "value": "current" + } + ], + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fontSize": "100%", + "hideTimeOverride": true, + "id": 96, + "links": [], + "pageSize": null, + "scroll": false, + "showHeader": true, + "sort": { + "col": null, + "desc": false + }, + "span": 3, + "styles": [ + { + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Metric", + "sanitize": false, + "type": "string" + }, + { + "colorMode": "cell", + "colors": [ + "rgba(50, 172, 45, 0.97)", + "rgba(237, 129, 40, 0.89)", + "rgba(245, 54, 54, 0.9)" + ], + "decimals": 0, + "pattern": "Current", + "thresholds": [ + "1", + "2" + ], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_disconnected_count\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Disconnect Stores", + "refId": "B", + "step": 20 + }, + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_unhealth_count\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Unhealth Stores", + "refId": "C", + "step": 20 + }, + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_low_space_count\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "LowSpace Stores", + "refId": "D", + "step": 20 + }, + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_down_count\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Down Stores", + "refId": "E", + "step": 20 + }, + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_offline_count\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Offline Stores", + "refId": "F", + "step": 20 + }, + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_tombstone_count\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Tombstone Stores", + "refId": "G", + "step": 20 + } + ], + "timeFrom": "1s", + "title": "Abnormal Stores", + "transform": "timeseries_aggregations", + "transparent": false, + "type": "table" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 9, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "used ratio", + "yaxis": 2 + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"storage_size\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "storage size", + "refId": "A", + "step": 4 + }, + { + "expr": "avg(pd_cluster_status{type=\"storage_size\", namespace=~\"$namespace\"}) / avg(pd_cluster_status{type=\"storage_capacity\", namespace=~\"$namespace\"})", + "format": "time_series", + "hide": false, + "intervalFactor": 4, + "legendFormat": "used ratio", + "refId": "B", + "step": 8 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Current Storage Usage", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 18, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_count\"})", + "intervalFactor": 2, + "legendFormat": "count", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Current Peer Count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 76, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_cluster_metadata{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Metadata Information", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 75, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_regions_label_level{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region Label Isolation Level", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 100 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "60s", + "handler": 1, + "message": "Regions are unhealthy", + "name": "region health alert", + "noDataState": "keep_state", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 72, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_regions_status{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + }, + { + "expr": "sum(pd_regions_status) by (instance, type)", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 100 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Region Health", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Cluster", + "titleSize": "h4" + }, + { + "collapse": true, + "height": 214, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 83, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_capacity\"}", + "format": "time_series", + "hide": false, + "instant": false, + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store capacity", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 91, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_available\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store available", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 90, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_used\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store used", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 85, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_size\"}) by (store) / sum(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_used\"}) by (store) * 2^20", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Size amplification", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 84, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_available\"}) by (store) / sum(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_capacity\"}) by (store)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store available ratio", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 40, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_score\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 1000000000 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Store leader score", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 41, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": false, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_score\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 1000000000 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Store region score", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 56, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_size\"}", + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store leader size", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "mbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 57, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_size\"}", + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store region size", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "mbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 58, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_count\"}", + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store leader count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 59, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_count\"}", + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store region count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Balance", + "titleSize": "h4" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 0, + "fill": 0, + "id": 50, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_write_region_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot write region's leader distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 0, + "fill": 0, + "id": 51, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_write_region_as_peer\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot write region's peer distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "fill": 1, + "id": 48, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"total_written_bytes_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "metric": "pd_hotspot_status", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot write region's leader written bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "fill": 1, + "id": 49, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"total_written_bytes_as_peer\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot region's peer written bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 0, + "fill": 0, + "id": 60, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_read_region_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot read region's leader distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 0, + "fill": 0, + "id": 61, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_read_region_as_peer\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot read region's peer distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 62, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"total_read_bytes_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "metric": "pd_hotspot_status", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot read region's leader read bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 63, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"total_read_bytes_as_peer\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot read region's peer read bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "HotRegion", + "titleSize": "h4" + }, + { + "collapse": true, + "height": 288, + "panels": [ + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 46, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 1, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_status{type=\"allow\",instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{kind}}", + "metric": "pd_scheduler_status", + "refId": "A", + "step": 2 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler is running", + "tooltip": { + "shared": true, + "sort": 1, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 87, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "total", + "sortDesc": true, + "total": true, + "values": true + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_balance_leader{instance=\"$instance\", type=\"move_leader\"}[30s])) by (store)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance leader movement", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "hideTimeOverride": false, + "id": 86, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "total", + "sortDesc": true, + "total": true, + "values": true + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_balance_region{instance=\"$instance\", type=\"move_peer\"}[1m])) by (store)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance region movement", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 89, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": true, + "values": true + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_balance_leader{instance=\"$instance\", type!=\"move_leader\"}[30s])) by (type, store)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}_{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance leader event", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 88, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sortDesc": false, + "total": true, + "values": true + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_balance_region{instance=\"$instance\", type!=\"move_peer\"}[30s])) by (type, store)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}_{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance region event", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 52, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_event_count{instance=\"$instance\", type=\"balance-leader-scheduler\"}[5m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "pd_scheduler_event_count", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance leader scheduler", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 53, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_event_count{instance=\"$instance\", type=\"balance-region-scheduler\"}[5m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "pd_scheduler_event_count", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance region scheduler", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 69, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_checker_event_count{instance=\"$instance\", type=\"namespace_checker\"}[1m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Namespace checker", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 70, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_checker_event_count{instance=\"$instance\", type=\"replica_checker\"}[1m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Replica checker", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 71, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_checker_event_count{instance=\"$instance\", type=\"merge_checker\"}[1m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region merge checker", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Scheduler", + "titleSize": "h4" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 45, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"create\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operator create", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 79, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"check\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operator check", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 77, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"finish\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operator finish", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 78, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"timeout\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operator timeout", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 80, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"canceled\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"replaced\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operator replaced or canceled", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 47, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\"}[1m])) by (event)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{event}}", + "metric": "pd_scheduler_status", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operators count by state", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 67, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(pd_schedule_finish_operators_duration_seconds_bucket[5m])) by (type, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% operator finish duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 68, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.5, sum(rate(pd_schedule_finish_operators_duration_seconds_bucket[5m])) by (type, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "50% operator finish duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 81, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(pd_schedule_finish_operator_steps_duration_seconds_bucket[5m])) by (type, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% operator step finish duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "dtdurations", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 82, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.5, sum(rate(pd_schedule_finish_operator_steps_duration_seconds_bucket[5m])) by (type, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "50% operator step finish duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "dtdurations", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Operator", + "titleSize": "h4" + }, + { + "collapse": true, + "height": "300px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(grpc_server_handling_seconds_count{instance=\"$instance\"}[1m])) by (grpc_method)", + "intervalFactor": 2, + "legendFormat": "{{grpc_method}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "completed commands rate", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 10, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 2, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(grpc_server_handling_seconds_bucket{instance=\"$instance\"}[5m])) by (grpc_method, le))", + "intervalFactor": 2, + "legendFormat": "{{grpc_method}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% completed_cmds_duration_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Grpc", + "titleSize": "h4" + }, + { + "collapse": true, + "height": "300px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 5, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_txn_handle_txns_duration_seconds_count[5m])) by (instance, result)", + "intervalFactor": 2, + "legendFormat": "{{instance}} : {{result}}", + "refId": "A", + "step": 2 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "handle_txns_count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 6, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(pd_txn_handle_txns_duration_seconds_bucket[5m])) by (instance, result, le))", + "intervalFactor": 2, + "legendFormat": "{{instance}} {{result}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% handle_txns_duration_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 7, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m])) by (instance, le))", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% wal_fsync_duration_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 34, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(etcd_network_peer_round_trip_time_seconds_bucket[5m])) by (instance, le))", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% peer_round_trip_time_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 0.1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "datasourceId": 1, + "model": { + "expr": "delta(etcd_disk_wal_fsync_duration_seconds_count[1m])", + "intervalFactor": 2, + "legendFormat": "{{instance}} etch disk wal fsync rate", + "refId": "A", + "step": 4 + }, + "params": [ + "A", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "60s", + "handler": 1, + "message": "PD etcd disk fsync maybe is down.", + "name": "etcd disk fsync", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 44, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "delta(etcd_disk_wal_fsync_duration_seconds_count[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} etch disk wal fsync rate", + "refId": "A", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 0.1 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "etcd disk wal fsync rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Etcd", + "titleSize": "h4" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 28, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_client_request_handle_requests_duration_seconds_count[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 2 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "handle_requests_count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 29, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "sort": "current", + "sortDesc": false, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.98, sum(rate(pd_client_request_handle_requests_duration_seconds_bucket[30s])) by (type, le))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{type}} 98th percentile", + "refId": "A", + "step": 2 + }, + { + "expr": "avg(rate(pd_client_request_handle_requests_duration_seconds_sum[30s])) by (type) / avg(rate(pd_client_request_handle_requests_duration_seconds_count[30s])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}} average", + "refId": "B", + "step": 2 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "handle_requests_duration_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "TiDB", + "titleSize": "h4" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 54, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_region_heartbeat{instance=\"$instance\", type=\"report\", status=\"ok\"}[1m])) by (store)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region heartbeat report", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 77, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_region_heartbeat{instance=\"$instance\", type=\"report\", status=\"err\"}[1m])) by (store)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region heartbeat report error", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 78, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_region_heartbeat{instance=\"$instance\", type=\"report\", status=\"bind\"}[1m])) by (store)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region heartbeat report active", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 64, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_scheduler_region_heartbeat{type=\"push\",instance=\"$instance\"}[5m])*60) by (store, status)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "store{{store}}-{{status}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region schedule push", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 74, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(pd_scheduler_region_heartbeat_latency_seconds_bucket[5m])) by (store, le))", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% region heartbeat latency", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Heartbeat", + "titleSize": "h4" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "${DS_TEST-CLUSTER}", + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "instance", + "options": [], + "query": "label_values(pd_cluster_status, instance)", + "refresh": 1, + "regex": "", + "sort": 0, + "tagValuesQuery": null, + "tags": [], + "tagsQuery": null, + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": {}, + "datasource": "${DS_TEST-CLUSTER}", + "hide": 0, + "includeAll": true, + "label": "Namespace", + "multi": false, + "name": "namespace", + "options": [], + "query": "label_values(pd_cluster_status{instance=\"$instance\"}, namespace)", + "refresh": 1, + "regex": "", + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "Test-Cluster-PD", + "version": 55 +} diff --git a/docker/tidb/config/pd-nightly-tiflash.toml b/docker/tidb/config/pd-nightly-tiflash.toml new file mode 100755 index 0000000..4061e60 --- /dev/null +++ b/docker/tidb/config/pd-nightly-tiflash.toml @@ -0,0 +1,3 @@ +[replication] +enable-placement-rules = true +max-replicas = 1 diff --git a/docker/tidb/config/pd.rules.yml b/docker/tidb/config/pd.rules.yml new file mode 100755 index 0000000..ddb94aa --- /dev/null +++ b/docker/tidb/config/pd.rules.yml @@ -0,0 +1,146 @@ +groups: +- name: alert.rules + rules: + - alert: PD_cluster_offline_tikv_nums + expr: sum ( pd_cluster_status{type="store_down_count"} ) > 0 + for: 1m + labels: + env: test-cluster + level: emergency + expr: sum ( pd_cluster_status{type="store_down_count"} ) > 0 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: PD_cluster_offline_tikv_nums + + - alert: PD_etcd_write_disk_latency + expr: histogram_quantile(0.99, sum(rate(etcd_disk_wal_fsync_duration_seconds_bucket[1m])) by (instance,job,le) ) > 1 + for: 1m + labels: + env: test-cluster + level: critical + expr: histogram_quantile(0.99, sum(rate(etcd_disk_wal_fsync_duration_seconds_bucket[1m])) by (instance,job,le) ) > 1 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: PD_etcd_write_disk_latency + + - alert: PD_miss_peer_region_count + expr: sum( pd_regions_status{type="miss_peer_region_count"} ) > 100 + for: 1m + labels: + env: test-cluster + level: critical + expr: sum( pd_regions_status{type="miss_peer_region_count"} ) > 100 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: PD_miss_peer_region_count + + - alert: PD_cluster_lost_connect_tikv_nums + expr: sum ( pd_cluster_status{type="store_disconnected_count"} ) > 0 + for: 1m + labels: + env: test-cluster + level: warning + expr: sum ( pd_cluster_status{type="store_disconnected_count"} ) > 0 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: PD_cluster_lost_connect_tikv_nums + + - alert: PD_cluster_low_space + expr: sum ( pd_cluster_status{type="store_low_space_count"} ) > 0 + for: 1m + labels: + env: test-cluster + level: warning + expr: sum ( pd_cluster_status{type="store_low_space_count"} ) > 0 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: PD_cluster_low_space + + - alert: PD_etcd_network_peer_latency + expr: histogram_quantile(0.99, sum(rate(etcd_network_peer_round_trip_time_seconds_bucket[1m])) by (To,instance,job,le) ) > 1 + for: 1m + labels: + env: test-cluster + level: warning + expr: histogram_quantile(0.99, sum(rate(etcd_network_peer_round_trip_time_seconds_bucket[1m])) by (To,instance,job,le) ) > 1 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: PD_etcd_network_peer_latency + + - alert: PD_tidb_handle_requests_duration + expr: histogram_quantile(0.99, sum(rate(pd_client_request_handle_requests_duration_seconds_bucket{type="tso"}[1m])) by (instance,job,le) ) > 0.1 + for: 1m + labels: + env: test-cluster + level: warning + expr: histogram_quantile(0.99, sum(rate(pd_client_request_handle_requests_duration_seconds_bucket{type="tso"}[1m])) by (instance,job,le) ) > 0.1 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: PD_tidb_handle_requests_duration + + - alert: PD_down_peer_region_nums + expr: sum ( pd_regions_status{type="down_peer_region_count"} ) > 0 + for: 1m + labels: + env: test-cluster + level: warning + expr: sum ( pd_regions_status{type="down_peer_region_count"} ) > 0 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: PD_down_peer_region_nums + + - alert: PD_incorrect_namespace_region_count + expr: sum ( pd_regions_status{type="incorrect_namespace_region_count"} ) > 100 + for: 1m + labels: + env: test-cluster + level: warning + expr: sum ( pd_regions_status{type="incorrect_namespace_region_count"} ) > 0 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: PD_incorrect_namespace_region_count + + - alert: PD_pending_peer_region_count + expr: sum( pd_regions_status{type="pending_peer_region_count"} ) > 100 + for: 1m + labels: + env: test-cluster + level: warning + expr: sum( pd_regions_status{type="pending_peer_region_count"} ) > 100 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: PD_pending_peer_region_count + + - alert: PD_leader_change + expr: count( changes(pd_server_tso{type="save"}[10m]) > 0 ) >= 2 + for: 1m + labels: + env: test-cluster + level: warning + expr: count( changes(pd_server_tso{type="save"}[10m]) > 0 ) >= 2 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: PD_leader_change + + - alert: TiKV_space_used_more_than_80% + expr: sum(pd_cluster_status{type="storage_size"}) / sum(pd_cluster_status{type="storage_capacity"}) * 100 > 80 + for: 1m + labels: + env: test-cluster + level: warning + expr: sum(pd_cluster_status{type="storage_size"}) / sum(pd_cluster_status{type="storage_capacity"}) * 100 > 80 + annotations: + description: 'cluster: test-cluster, type: {{ $labels.type }}, instance: {{ $labels.instance }}, values: {{ $value }}' + value: '{{ $value }}' + summary: TiKV_space_used_more_than_80% diff --git a/docker/tidb/config/pd.toml b/docker/tidb/config/pd.toml new file mode 100755 index 0000000..b1562a5 --- /dev/null +++ b/docker/tidb/config/pd.toml @@ -0,0 +1,86 @@ +# PD Configuration. + +name = "pd" +data-dir = "default.pd" + +client-urls = "http://127.0.0.1:2379" +# if not set, use ${client-urls} +advertise-client-urls = "" + +peer-urls = "http://127.0.0.1:2380" +# if not set, use ${peer-urls} +advertise-peer-urls = "" + +initial-cluster = "pd=http://127.0.0.1:2380" +initial-cluster-state = "new" + +lease = 3 +tso-save-interval = "3s" + +[security] +# Path of file that contains list of trusted SSL CAs. if set, following four settings shouldn't be empty +cacert-path = "" +# Path of file that contains X509 certificate in PEM format. +cert-path = "" +# Path of file that contains X509 key in PEM format. +key-path = "" + +[log] +level = "error" + +# log format, one of json, text, console +#format = "text" + +# disable automatic timestamps in output +#disable-timestamp = false + +# file logging +[log.file] +#filename = "" +# max log file size in MB +#max-size = 300 +# max log file keep days +#max-days = 28 +# maximum number of old log files to retain +#max-backups = 7 +# rotate log by day +#log-rotate = true + +[metric] +# prometheus client push interval, set "0s" to disable prometheus. +interval = "15s" +# prometheus pushgateway address, leaves it empty will disable prometheus. +address = "pushgateway:9091" + +[schedule] +max-merge-region-size = 0 +split-merge-interval = "1h" +max-snapshot-count = 3 +max-pending-peer-count = 16 +max-store-down-time = "30m" +leader-schedule-limit = 4 +region-schedule-limit = 4 +replica-schedule-limit = 8 +merge-schedule-limit = 8 +tolerant-size-ratio = 5.0 + +# customized schedulers, the format is as below +# if empty, it will use balance-leader, balance-region, hot-region as default +# [[schedule.schedulers]] +# type = "evict-leader" +# args = ["1"] + +[replication] +# The number of replicas for each region. +max-replicas = 3 +# The label keys specified the location of a store. +# The placement priorities is implied by the order of label keys. +# For example, ["zone", "rack"] means that we should place replicas to +# different zones first, then to different racks if we don't have enough zones. +location-labels = [] + +[label-property] +# Do not assign region leaders to stores that have these tags. +# [[label-property.reject-leader]] +# key = "zone" +# value = "cn1 diff --git a/docker/tidb/config/prometheus.yml b/docker/tidb/config/prometheus.yml new file mode 100755 index 0000000..c6eaf2e --- /dev/null +++ b/docker/tidb/config/prometheus.yml @@ -0,0 +1,15 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s +scrape_configs: + - job_name: 'tidb-cluster' + scrape_interval: 5s + honor_labels: true + static_configs: + - targets: ['pushgateway:9091'] + labels: + cluster: 'tidb-cluster' +rule_files: + - 'pd.rules.yml' + - 'tikv.rules.yml' + - 'tidb.rules.yml' diff --git a/docker/tidb/config/pump.toml b/docker/tidb/config/pump.toml new file mode 100755 index 0000000..7858b52 --- /dev/null +++ b/docker/tidb/config/pump.toml @@ -0,0 +1,45 @@ +# pump Configuration. + + # addr(i.e. 'host:port') to listen on for client traffic +addr = "127.0.0.1:8250" + + # addr(i.e. 'host:port') to advertise to the public +advertise-addr = "" + + # a integer value to control expiry date of the binlog data, indicates for how long (in days) the binlog data would be stored. +# must bigger than 0 +gc = 7 + + # path to the data directory of pump's data +data-dir = "data.pump" + + # number of seconds between heartbeat ticks (in 2 seconds) +heartbeat-interval = 2 + + # a comma separated list of PD endpoints +pd-urls = "http://127.0.0.1:2379" + + #[security] +# Path of file that contains list of trusted SSL CAs for connection with cluster components. +# ssl-ca = "/path/to/ca.pem" +# Path of file that contains X509 certificate in PEM format for connection with cluster components. +# ssl-cert = "/path/to/drainer.pem" +# Path of file that contains X509 key in PEM format for connection with cluster components. +# ssl-key = "/path/to/drainer-key.pem" +# +# [storage] +# Set to `true` (default) for best reliability, which prevents data loss when there is a power failure. +# sync-log = true +# +# we suggest using the default config of the embedded LSM DB now, do not change it useless you know what you are doing +# [storage.kv] +# block-cache-capacity = 8388608 +# block-restart-interval = 16 +# block-size = 4096 +# compaction-L0-trigger = 8 +# compaction-table-size = 67108864 +# compaction-total-size = 536870912 +# compaction-total-size-multiplier = 8.0 +# write-buffer = 67108864 +# write-L0-pause-trigger = 24 +# write-L0-slowdown-trigger = 17 diff --git a/docker/tidb/config/spark-defaults.conf b/docker/tidb/config/spark-defaults.conf new file mode 100755 index 0000000..76467a5 --- /dev/null +++ b/docker/tidb/config/spark-defaults.conf @@ -0,0 +1,2 @@ +spark.tispark.pd.addresses pd0:2379 +spark.sql.extensions org.apache.spark.sql.TiExtensions diff --git a/docker/tidb/config/tidb-dashboard.json b/docker/tidb/config/tidb-dashboard.json new file mode 100755 index 0000000..0266d8a --- /dev/null +++ b/docker/tidb/config/tidb-dashboard.json @@ -0,0 +1,6928 @@ +{ + "__inputs": [ + { + "name": "DS_TEST-CLUSTER", + "label": "test-cluster", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "4.6.3" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "${DS_TEST-CLUSTER}", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "hideControls": false, + "id": null, + "links": [ + { + "icon": "doc", + "includeVars": true, + "keepTime": true, + "tags": [], + "targetBlank": true, + "title": "Report", + "tooltip": "Open a pdf report for the current dashboard", + "type": "link", + "url": "http://172.16.10.49:8686/api/report/test-cluster-tidb?apitoken=eyJrIjoiQkU2bmFQdjZMeTRmWVJ3UFo0bmV3Ym5zRG04Y2hmMHYiLCJuIjoiZ3JhZmFuYV9hcGlrZXkiLCJpZCI6MX0=" + } + ], + "refresh": "30s", + "rows": [ + { + "collapse": false, + "height": "240", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 80, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "95", + "refId": "C" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 42, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": false, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_server_query_total[1m])) by (result)", + "format": "time_series", + "instant": false, + "intervalFactor": 2, + "legendFormat": "query {{result}}", + "refId": "A", + "step": 60 + }, + { + "expr": "sum(rate(tidb_server_query_total{result=\"OK\"}[1m] offset 1d))", + "format": "time_series", + "hide": true, + "instant": false, + "intervalFactor": 3, + "legendFormat": "yesterday", + "refId": "B", + "step": 90 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "QPS", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 21, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_executor_statement_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Statement OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 2, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(tidb_server_query_total[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} {{type}} {{result}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "QPS By Instance", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 92, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_server_execute_error_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": " {{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Failed Query OPM", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Query Summary", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 23, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "B", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration 80 By Instance", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ instance }}", + "refId": "B", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration 95 By Instance", + "tooltip": { + "msResolution": true, + "shared": false, + "sort": 0, + "value_type": "cumulative" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + "max" + ] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 25, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration 99 By Instance", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 81, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration 999 By Instance", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 94, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "increase(tidb_server_execute_error_total[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}} @ {{instance}}", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Failed Query OPM Detail", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "description": "The internal SQL is used by TiDB itself.", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 68, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_session_restricted_sql_total[30s]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Internal SQL OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Query Detail", + "titleSize": "h6" + }, + { + "collapse": false, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 8, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "fill": 0, + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": true, + "targets": [ + { + "expr": "tidb_server_connections", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 40 + }, + { + "expr": "sum(tidb_server_connections)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "total", + "refId": "B", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Connection Count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 3, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "go_memstats_heap_inuse_bytes{job=~\"tidb.*\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "go_memstats_heap_inuse_bytes", + "refId": "B", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Heap Memory Usage", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 61, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": " go_goroutines{job=~\"tidb.*\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Goroutine Count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "description": "TiDB Server events.", + "fill": 1, + "id": 49, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "increase(tidb_server_event_total[10m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Events OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 54, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "increase(tidb_server_panic_total[1m])", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "panic-{{instance}}", + "refId": "A" + }, + { + "expr": "increase(tidb_server_critical_error_total[1m])", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "critical-{{instance}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Uncommon Error OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 82, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_monitor_keep_alive_total[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Keep Alive OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Server", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 69, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_session_transaction_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Transaction OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 72, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "95", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 74, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": " histogram_quantile(0.99, sum(rate(tidb_session_transaction_statement_num_bucket[30s])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "A" + }, + { + "expr": " histogram_quantile(0.80, sum(rate(tidb_session_transaction_statement_num_bucket[30s])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Transaction Statement Num", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 36, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_session_retry_error_total[30s]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "retry error", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Session Retry Error OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 67, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1.0, sum(rate(tidb_session_retry_num_bucket[30s])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "100", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_session_retry_num_bucket[30s])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_session_retry_num_bucket[30s])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Transaction Retry Num", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Transaction", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 301, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 76, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_executor_expensive_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Expensive Executors OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 91, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_server_plan_cache_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Queries Using Plan Cache OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Executor", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 12, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [ + { + "type": "dashboard" + } + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_distsql_handle_query_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999-{{type}}", + "refId": "D" + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_distsql_handle_query_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "99-{{type}}", + "metric": "tidb_distsql_handle_query_duration_seconds_bucket", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_distsql_handle_query_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90-{{type}}", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(tidb_distsql_handle_query_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "50-{{type}}", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Distsql Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.0005", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_distsql_handle_query_duration_seconds_count[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "metric": "tidb_distsql_query_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Distsql QPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 60, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_distsql_scan_keys_partial_num_count[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "metric": "tidb_distsql_query_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Distsql Partial QPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 57, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_distsql_scan_keys_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "100", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_distsql_scan_keys_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(tidb_distsql_scan_keys_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scan Keys Num", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 58, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_distsql_scan_keys_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "100", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_distsql_scan_keys_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_distsql_scan_keys_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scan Keys Partial Num", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 59, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_distsql_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "100", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_distsql_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(tidb_distsql_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Partial Num", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 40, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "total", + "sortDesc": true, + "total": true, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_cop_actions_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Coprocessor Count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 41, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_tikvclient_cop_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Coprocessor Seconds 999", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Distsql", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 6, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tidb_tikvclient_backoff_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "9999", + "refId": "A", + "step": 40 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_tikvclient_backoff_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_tikvclient_backoff_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Retry Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 11, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_region_err_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tidb_server_session_execute_parse_duration_count", + "refId": "A", + "step": 40 + }, + { + "expr": "sum(rate(tidb_tikvclient_region_err_total{type=\"server_is_busy\"}[1m]))", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "sum", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "TiClient Region Error OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 53, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": true, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_backoff_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Backoff OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 32, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_lock_resolver_actions_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tidb_tikvclient_lock_resolver_actions_total", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Lock Resolve OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 84, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_lock_cleanup_task_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "cleanup_secondary_failure_{{type}}", + "metric": "tidb_tikvclient_lock_resolver_actions_total", + "refId": "A", + "step": 40 + }, + { + "expr": "sum(rate(tidb_tikvclient_load_safepoint_total{type=\"fail\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "load_safepoint_failure", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Other Errors OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "KV Errors", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 48, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tidb_tikvclient_request_seconds_bucket{type!=\"GC\"}[1m])) by (le, store))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Request Duration 999 by store", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 30, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_tikvclient_request_seconds_bucket{type!=\"GC\"}[1m])) by (le,type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Request Duration 999 by type", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 22, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_tikvclient_txn_cmd_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Cmd Duration 9999", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 18, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_tikvclient_txn_cmd_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Cmd Duration 99", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "KV Duration", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 5, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_txn_cmd_duration_seconds_count[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Cmd OPS", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 4, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_txn_total[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Txn OPS", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 44, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_tikvclient_txn_regions_num_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Txn Regions Num 90", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 34, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "avg", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_tikvclient_txn_write_size_bytes_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Txn Write Size Bytes 100", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 33, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "avg", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_tikvclient_txn_write_kv_num_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "100 {{instance}}", + "refId": "B", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Txn Write KV Num 100", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 83, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "avg", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_load_safepoint_total{type=\"ok\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "B", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Load Safepoint OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "KV Count", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 20, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_client_cmd_handle_cmds_duration_seconds_count{type!=\"tso\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD Client CMD OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "description": "", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 35, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type!=\"tso\"}[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999-{{type}}", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type!=\"tso\"}[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99-{{type}}", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type!=\"tso\"}[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90-{{type}}", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD Client CMD Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 43, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_client_cmd_handle_failed_cmds_duration_seconds_count[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD Client CMD Fail OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "description": "The duration of a client calling GetTSAsync until received the TS result.", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 79, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_client_cmd_handle_cmds_duration_seconds_count{type=\"tso\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "cmd", + "refId": "C" + }, + { + "expr": "sum(rate(pd_client_request_handle_requests_duration_seconds_count{type=\"tso\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "request", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD TSO OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "description": "The duration of a client calling GetTSAsync until received the TS result.", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 77, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD TSO Wait Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "description": "The duration of a client sending TSO request until received the response.", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 78, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(pd_client_request_handle_requests_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(pd_client_request_handle_requests_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(pd_client_request_handle_requests_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD TSO RPC Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "PD Client", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 27, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_domain_load_schema_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Load Schema Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 28, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "/.*failed/", + "bars": true + } + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_domain_load_schema_total[1m])) by (instance,type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{type}}", + "metric": "tidb_domain_load_schema_duration_count", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Load Schema OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 29, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_session_schema_lease_error_total[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tidb_server_", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schema Lease Error OPM", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Schema Load", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 9, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_ddl_handle_job_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "DDL Duration 95", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 63, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_ddl_batch_add_idx_succ_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Batch Add Index Duration 100", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 62, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "tidb_ddl_waiting_jobs", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "DDL Waiting Jobs Count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 55, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "increase(tidb_ddl_worker_operation_total[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "DDL OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 56, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(increase(tidb_ddl_worker_operation_duration_seconds_bucket[1m])) by (le, type, result))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}-{{result}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "DDL Worker Duration 99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 64, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_ddl_deploy_syncer_duration_seconds_bucket[2m])) by (le, type, result))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}-{{result}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Deploy Syncer Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 65, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_ddl_owner_handle_syncer_duration_seconds_bucket[2m])) by (le, type, result))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}-{{result}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Owner Handle Syncer Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 66, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_ddl_update_self_ver_duration_seconds_bucket[2m])) by (le, result))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{result}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Update Self Version Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "DDL", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 46, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_statistics_auto_analyze_duration_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "auto analyze duration", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Auto Analyze Duration 95", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 47, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_statistics_auto_analyze_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Auto Analyze QPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 70, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_statistics_stats_inaccuracy_rate_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "A", + "step": 30 + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_statistics_stats_inaccuracy_rate_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(tidb_statistics_stats_inaccuracy_rate_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Stats Inaccuracy Rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 71, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_statistics_pseudo_estimation_total[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Pseudo Estimation OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 92, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_statistics_dump_feedback_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Dump Feedback OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 93, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_statistics_update_stats_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Update Stats OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Statistics", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 50, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_autoid_operation_duration_seconds_count[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "AutoID QPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 51, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_autoid_operation_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99-{{type}}", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_autoid_operation_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80-{{type}}", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "AutoID Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 52, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_meta_operation_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Meta Operations Duration 99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Meta", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 85, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_tikvclient_gc_worker_actions_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Worker Action OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 86, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_tikvclient_gc_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration 99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 87, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "max(tidb_tikvclient_gc_config) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Config", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 88, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_tikvclient_gc_failure[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GC Failure OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 89, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_tikvclient_gc_action_result[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Action Result OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 90, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_tikvclient_gc_region_too_many_locks[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Too Many Locks Error OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "GC", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "Test-Cluster-TiDB", + "version": 5 +} diff --git a/docker/tidb/config/tidb.rules.yml b/docker/tidb/config/tidb.rules.yml new file mode 100755 index 0000000..39e19a3 --- /dev/null +++ b/docker/tidb/config/tidb.rules.yml @@ -0,0 +1,134 @@ +groups: +- name: alert.rules + rules: + - alert: TiDB_schema_error + expr: increase(tidb_session_schema_lease_error_total{type="outdated"}[15m]) > 0 + for: 1m + labels: + env: test-cluster + level: emergency + expr: increase(tidb_session_schema_lease_error_total{type="outdated"}[15m]) > 0 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiDB schema error + + - alert: TiDB_tikvclient_region_err_total + expr: increase( tidb_tikvclient_region_err_total[10m] ) > 6000 + for: 1m + labels: + env: test-cluster + level: emergency + expr: increase( tidb_tikvclient_region_err_total[10m] ) > 6000 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiDB tikvclient_backoff_count error + + - alert: TiDB_domain_load_schema_total + expr: increase( tidb_domain_load_schema_total{type="failed"}[10m] ) > 10 + for: 1m + labels: + env: test-cluster + level: emergency + expr: increase( tidb_domain_load_schema_total{type="failed"}[10m] ) > 10 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiDB domain_load_schema_total error + + - alert: TiDB_monitor_keep_alive + expr: increase(tidb_monitor_keep_alive_total{job="tidb"}[10m]) < 100 + for: 1m + labels: + env: test-cluster + level: emergency + expr: increase(tidb_monitor_keep_alive_total{job="tidb"}[10m]) < 100 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiDB monitor_keep_alive error + + - alert: TiDB_server_panic_total + expr: increase(tidb_server_panic_total[10m]) > 0 + for: 1m + labels: + env: test-cluster + level: critical + expr: increase(tidb_server_panic_total[10m]) > 0 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiDB server panic total + + - alert: TiDB_memory_abnormal + expr: go_memstats_heap_inuse_bytes{job="tidb"} > 1e+10 + for: 1m + labels: + env: test-cluster + level: warning + expr: go_memstats_heap_inuse_bytes{job="tidb"} > 1e+10 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiDB heap memory usage is over 10 GB + + - alert: TiDB_query_duration + expr: histogram_quantile(0.99, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) BY (le, instance)) > 1 + for: 1m + labels: + env: test-cluster + level: warning + expr: histogram_quantile(0.99, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) BY (le, instance)) > 1 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiDB query duration 99th percentile is above 1s + + - alert: TiDB_server_event_error + expr: increase(tidb_server_server_event{type=~"server_start|server_hang"}[15m]) > 0 + for: 1m + labels: + env: test-cluster + level: warning + expr: increase(tidb_server_server_event{type=~"server_start|server_hang"}[15m]) > 0 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiDB server event error + + - alert: TiDB_tikvclient_backoff_count + expr: increase( tidb_tikvclient_backoff_count[10m] ) > 10 + for: 1m + labels: + env: test-cluster + level: warning + expr: increase( tidb_tikvclient_backoff_count[10m] ) > 10 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiDB tikvclient_backoff_count error + + - alert: TiDB_monitor_time_jump_back_error + expr: increase(tidb_monitor_time_jump_back_total[10m]) > 0 + for: 1m + labels: + env: test-cluster + level: warning + expr: increase(tidb_monitor_time_jump_back_total[10m]) > 0 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiDB monitor time_jump_back error + + - alert: TiDB_ddl_waiting_jobs + expr: sum(tidb_ddl_waiting_jobs) > 5 + for: 1m + labels: + env: test-cluster + level: warning + expr: sum(tidb_ddl_waiting_jobs) > 5 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiDB ddl waiting_jobs too much diff --git a/docker/tidb/config/tidb.toml b/docker/tidb/config/tidb.toml new file mode 100755 index 0000000..9f881b4 --- /dev/null +++ b/docker/tidb/config/tidb.toml @@ -0,0 +1,239 @@ +# TiDB Configuration. + +# TiDB server host. +host = "0.0.0.0" + +# TiDB server port. +port = 4000 + +# Registered store name, [tikv, mocktikv] +store = "mocktikv" + +# TiDB storage path. +path = "/tmp/tidb" + +# The socket file to use for connection. +socket = "" + +# Run ddl worker on this tidb-server. +run-ddl = true + +# Schema lease duration, very dangerous to change only if you know what you do. +lease = "0" + +# When create table, split a separated region for it. It is recommended to +# turn off this option if there will be a large number of tables created. +split-table = true + +# The limit of concurrent executed sessions. +token-limit = 1000 + +# Only print a log when out of memory quota. +# Valid options: ["log", "cancel"] +oom-action = "log" + +# Set the memory quota for a query in bytes. Default: 32GB +mem-quota-query = 34359738368 + +# Enable coprocessor streaming. +enable-streaming = false + +# Set system variable 'lower_case_table_names' +lower-case-table-names = 2 + +[log] +# Log level: debug, info, warn, error, fatal. +level = "error" + +# Log format, one of json, text, console. +format = "text" + +# Disable automatic timestamp in output +disable-timestamp = false + +# Stores slow query log into separated files. +slow-query-file = "" + +# Queries with execution time greater than this value will be logged. (Milliseconds) +slow-threshold = 300 + +# Queries with internal result greater than this value will be logged. +expensive-threshold = 10000 + +# Maximum query length recorded in log. +query-log-max-len = 2048 + +# File logging. +[log.file] +# Log file name. +filename = "" + +# Max log file size in MB (upper limit to 4096MB). +max-size = 300 + +# Max log file keep days. No clean up by default. +max-days = 0 + +# Maximum number of old log files to retain. No clean up by default. +max-backups = 0 + +# Rotate log by day +log-rotate = true + +[security] +# Path of file that contains list of trusted SSL CAs for connection with mysql client. +ssl-ca = "" + +# Path of file that contains X509 certificate in PEM format for connection with mysql client. +ssl-cert = "" + +# Path of file that contains X509 key in PEM format for connection with mysql client. +ssl-key = "" + +# Path of file that contains list of trusted SSL CAs for connection with cluster components. +cluster-ssl-ca = "" + +# Path of file that contains X509 certificate in PEM format for connection with cluster components. +cluster-ssl-cert = "" + +# Path of file that contains X509 key in PEM format for connection with cluster components. +cluster-ssl-key = "" + +[status] +# If enable status report HTTP service. +report-status = true + +# TiDB status port. +status-port = 10080 + +# Prometheus pushgateway address, leaves it empty will disable prometheus push. +metrics-addr = "pushgateway:9091" + +# Prometheus client push interval in second, set \"0\" to disable prometheus push. +metrics-interval = 15 + +[performance] +# Max CPUs to use, 0 use number of CPUs in the machine. +max-procs = 0 +# StmtCountLimit limits the max count of statement inside a transaction. +stmt-count-limit = 5000 + +# Set keep alive option for tcp connection. +tcp-keep-alive = true + +# The maximum number of retries when commit a transaction. +retry-limit = 10 + +# Whether support cartesian product. +cross-join = true + +# Stats lease duration, which influences the time of analyze and stats load. +stats-lease = "3s" + +# Run auto analyze worker on this tidb-server. +run-auto-analyze = true + +# Probability to use the query feedback to update stats, 0 or 1 for always false/true. +feedback-probability = 0.0 + +# The max number of query feedback that cache in memory. +query-feedback-limit = 1024 + +# Pseudo stats will be used if the ratio between the modify count and +# row count in statistics of a table is greater than it. +pseudo-estimate-ratio = 0.7 + +[proxy-protocol] +# PROXY protocol acceptable client networks. +# Empty string means disable PROXY protocol, * means all networks. +networks = "" + +# PROXY protocol header read timeout, unit is second +header-timeout = 5 + +[plan-cache] +enabled = false +capacity = 2560 +shards = 256 + +[prepared-plan-cache] +enabled = false +capacity = 100 + +[opentracing] +# Enable opentracing. +enable = false + +# Whether to enable the rpc metrics. +rpc-metrics = false + +[opentracing.sampler] +# Type specifies the type of the sampler: const, probabilistic, rateLimiting, or remote +type = "const" + +# Param is a value passed to the sampler. +# Valid values for Param field are: +# - for "const" sampler, 0 or 1 for always false/true respectively +# - for "probabilistic" sampler, a probability between 0 and 1 +# - for "rateLimiting" sampler, the number of spans per second +# - for "remote" sampler, param is the same as for "probabilistic" +# and indicates the initial sampling rate before the actual one +# is received from the mothership +param = 1.0 + +# SamplingServerURL is the address of jaeger-agent's HTTP sampling server +sampling-server-url = "" + +# MaxOperations is the maximum number of operations that the sampler +# will keep track of. If an operation is not tracked, a default probabilistic +# sampler will be used rather than the per operation specific sampler. +max-operations = 0 + +# SamplingRefreshInterval controls how often the remotely controlled sampler will poll +# jaeger-agent for the appropriate sampling strategy. +sampling-refresh-interval = 0 + +[opentracing.reporter] +# QueueSize controls how many spans the reporter can keep in memory before it starts dropping +# new spans. The queue is continuously drained by a background go-routine, as fast as spans +# can be sent out of process. +queue-size = 0 + +# BufferFlushInterval controls how often the buffer is force-flushed, even if it's not full. +# It is generally not useful, as it only matters for very low traffic services. +buffer-flush-interval = 0 + +# LogSpans, when true, enables LoggingReporter that runs in parallel with the main reporter +# and logs all submitted spans. Main Configuration.Logger must be initialized in the code +# for this option to have any effect. +log-spans = false + +# LocalAgentHostPort instructs reporter to send spans to jaeger-agent at this address +local-agent-host-port = "" + +[tikv-client] +# Max gRPC connections that will be established with each tikv-server. +grpc-connection-count = 16 + +# After a duration of this time in seconds if the client doesn't see any activity it pings +# the server to see if the transport is still alive. +grpc-keepalive-time = 10 + +# After having pinged for keepalive check, the client waits for a duration of Timeout in seconds +# and if no activity is seen even after that the connection is closed. +grpc-keepalive-timeout = 3 + +# max time for commit command, must be twice bigger than raft election timeout. +commit-timeout = "41s" + +[binlog] + +# Socket file to write binlog. +binlog-socket = "" + +# WriteTimeout specifies how long it will wait for writing binlog to pump. +write-timeout = "15s" + +# If IgnoreError is true, when writting binlog meets error, TiDB would stop writting binlog, +# but still provide service. +ignore-error = false diff --git a/docker/tidb/config/tiflash-learner-nightly.toml b/docker/tidb/config/tiflash-learner-nightly.toml new file mode 100755 index 0000000..bb9cfa9 --- /dev/null +++ b/docker/tidb/config/tiflash-learner-nightly.toml @@ -0,0 +1,45 @@ +log-file = "/logs/tiflash_tikv.log" + +[readpool] + +[readpool.coprocessor] + +[readpool.storage] + +[server] +engine-addr = "tiflash:4030" +addr = "0.0.0.0:20280" +advertise-addr = "tiflash:20280" +#status-addr = "tiflash:20292" + +[storage] +data-dir = "/data/flash" + +[pd] + +[metric] + +[raftstore] +capacity = "10GB" + +[coprocessor] + +[rocksdb] +wal-dir = "" + +[rocksdb.defaultcf] + +[rocksdb.lockcf] + +[rocksdb.writecf] + +[raftdb] + +[raftdb.defaultcf] + +[security] +ca-path = "" +cert-path = "" +key-path = "" + +[import] diff --git a/docker/tidb/config/tiflash-nightly.toml b/docker/tidb/config/tiflash-nightly.toml new file mode 100755 index 0000000..2d87f12 --- /dev/null +++ b/docker/tidb/config/tiflash-nightly.toml @@ -0,0 +1,79 @@ +default_profile = "default" +display_name = "TiFlash" +listen_host = "0.0.0.0" +mark_cache_size = 5368709120 +tmp_path = "/data/tmp" +path = "/data" +tcp_port = 9110 +http_port = 8223 + +[flash] +tidb_status_addr = "tidb:10080" +service_addr = "tiflash:4030" + +[flash.flash_cluster] +cluster_manager_path = "/tiflash/flash_cluster_manager" +log = "/logs/tiflash_cluster_manager.log" +master_ttl = 60 +refresh_interval = 20 +update_rule_interval = 5 + +[flash.proxy] +config = "/tiflash-learner.toml" + +[status] +metrics_port = 8234 + +[logger] +errorlog = "/logs/tiflash_error.log" +log = "/logs/tiflash.log" +count = 20 +level = "debug" +size = "1000M" + +[application] +runAsDaemon = true + +[raft] +pd_addr = "pd0:2379" +storage_engine = "tmt" + +[quotas] + +[quotas.default] + +[quotas.default.interval] +duration = 3600 +errors = 0 +execution_time = 0 +queries = 0 +read_rows = 0 +result_rows = 0 + +[users] + +[users.default] +password = "" +profile = "default" +quota = "default" + +[users.default.networks] +ip = "::/0" + +[users.readonly] +password = "" +profile = "readonly" +quota = "default" + +[users.readonly.networks] +ip = "::/0" + +[profiles] + +[profiles.default] +load_balancing = "random" +max_memory_usage = 10000000000 +use_uncompressed_cache = 0 + +[profiles.readonly] +readonly = 1 diff --git a/docker/tidb/config/tikv-dashboard.json b/docker/tidb/config/tikv-dashboard.json new file mode 100755 index 0000000..0d8599e --- /dev/null +++ b/docker/tidb/config/tikv-dashboard.json @@ -0,0 +1,14099 @@ +{ + "__inputs": [ + { + "name": "DS_TEST-CLUSTER", + "label": "test-cluster", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "4.6.3" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "singlestat", + "name": "Singlestat", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "${DS_TEST-CLUSTER}", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "hideControls": false, + "id": null, + "links": [ + { + "icon": "doc", + "includeVars": true, + "keepTime": true, + "tags": [], + "targetBlank": true, + "title": "Report", + "tooltip": "Open a pdf report for the current dashboard", + "type": "link", + "url": "http://172.16.10.49:8686/api/report/test-cluster-tikv?apitoken=eyJrIjoiQkU2bmFQdjZMeTRmWVJ3UFo0bmV3Ym5zRG04Y2hmMHYiLCJuIjoiZ3JhZmFuYV9hcGlrZXkiLCJpZCI6MX0=" + } + ], + "refresh": "1m", + "rows": [ + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 5, + "grid": {}, + "id": 56, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 0, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_size_bytes) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "store size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 5, + "grid": {}, + "id": 1706, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 0, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_store_size_bytes{type=\"available\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "available size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 5, + "grid": {}, + "id": 1707, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 0, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_store_size_bytes{type=\"capacity\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "capacity size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1708, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total[1m])) by (instance,job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "cpu", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1709, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(process_resident_memory_bytes{job=~\"tikv.*\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "memory", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1710, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(node_disk_io_time_ms[1m]) / 1000", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - {{device}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "io utilization", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1711, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"kv\", type=\"wal_file_bytes\"}[1m])) by (job)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}-write", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"kv\", type=~\"bytes_read|iter_bytes_read\"}[1m])) by (job)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}-read", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "MBps", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1713, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": false, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_duration_seconds_count{type!=\"kv_gc\"}[1m])) by (job,type)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}} - {{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "QPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1712, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_fail_total{type!=\"kv_gc\"}[1m])) by (job)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}-grpc-msg-fail", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(delta(tikv_pd_heartbeat_message_total{type=\"noop\"}[1m])) by (job) < 1", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}-pd-heartbeat", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Errps", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1715, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{type=\"leader\"}) by (job)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 10 + }, + { + "expr": "delta(tikv_pd_heartbeat_tick_total{type=\"leader\"}[30s]) < -10", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "leader", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1714, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{type=\"region\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "region", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Cluster", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1584, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_too_busy_total[1m])) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "scheduler-{{job}}", + "metric": "", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(rate(tikv_channel_full_total[1m])) by (job, type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "channelfull-{{job}}-{{type}}", + "metric": "", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_coprocessor_request_error{type='full'}[1m])) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "coprocessor-{{job}}", + "metric": "", + "refId": "C", + "step": 4 + }, + { + "expr": "avg(tikv_engine_write_stall{type=\"write_stall_percentile99\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "stall-{{job}}", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "server is busy", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "10s", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "10s", + "handler": 1, + "message": "TiKV server report failures", + "name": "server report failures alert", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 18, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_server_report_failure_msg_total[1m])) by (type,instance,job,store_id)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}} - {{type}} - to - {{store_id}}", + "metric": "tikv_server_raft_store_msg_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "server report failures", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1718, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_storage_engine_async_request_total{status!~\"success|all\"}[1m])) by (job, status)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}-{{status}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "raftstore error", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1719, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_stage_total{stage=~\"snapshot_err|prepare_write_err\"}[1m])) by (job, stage)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}-{{stage}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler error", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1720, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_request_error[1m])) by (job, reason)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}-{{reason}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor error", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1721, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_fail_total[1m])) by (job, type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}-{{type}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor error", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1722, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(tikv_pd_heartbeat_tick_total{type=\"leader\"}[1m])) by (job)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "leader drop", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1723, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_raftstore_leader_missing) by (job)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "leader missing", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Errors", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 34, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{type=\"leader\"}) by (job)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 10 + }, + { + "expr": "delta(tikv_pd_heartbeat_tick_total{type=\"leader\"}[30s]) < -10", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "leader", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 37, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{type=\"region\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "region", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 3, + "grid": {}, + "id": 33, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_size_bytes) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "cf size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 5, + "grid": {}, + "id": 1705, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 0, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_size_bytes) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "store size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "datasourceId": 1, + "model": { + "expr": "sum(rate(tikv_channel_full_total[1m])) by (job, type)", + "intervalFactor": 2, + "legendFormat": "{{job}} - {{type}}", + "metric": "", + "refId": "A", + "step": 10 + }, + "params": [ + "A", + "10s", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "10s", + "handler": 1, + "message": "TiKV channel full", + "name": "TiKV channel full alert", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 3, + "grid": {}, + "id": 22, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_channel_full_total[1m])) by (job, type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}} - {{type}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "channel full", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1717, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_server_report_failure_msg_total[1m])) by (type,instance,job,store_id)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}} - {{type}} - to - {{store_id}}", + "metric": "tikv_server_raft_store_msg_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "server report failures", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 57, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_region_written_keys_sum[1m])) by (job) / sum(rate(tikv_region_written_keys_count[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_region_written_keys_bucket", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "region average written keys", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 58, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_region_written_bytes_sum[1m])) by (job) / sum(rate(tikv_region_written_bytes_count[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_regi", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "region average written bytes", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 75, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_region_written_keys_count[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_region_written_keys_bucket", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "active written leaders", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 1073741824 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "60s", + "handler": 1, + "name": "approximate region size alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1481, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_region_size_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_region_size_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_raftstore_region_size_sum[1m])) / sum(rate(tikv_raftstore_region_size_count[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "approximate region size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Server", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 31, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_apply_log_duration_seconds_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " 99%", + "metric": "", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_apply_log_duration_seconds_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_raftstore_apply_log_duration_seconds_sum[1m])) / sum(rate(tikv_raftstore_apply_log_duration_seconds_count[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "apply log duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 32, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_apply_log_duration_seconds_bucket[1m])) by (le, job))", + "intervalFactor": 2, + "legendFormat": " {{job}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "apply log duration per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 39, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_append_log_duration_seconds_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " 99%", + "metric": "", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_append_log_duration_seconds_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_raftstore_append_log_duration_seconds_sum[1m])) / sum(rate(tikv_raftstore_append_log_duration_seconds_count[1m]))", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "append log duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 40, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_append_log_duration_seconds_bucket[1m])) by (le, job))", + "intervalFactor": 2, + "legendFormat": "{{job}} ", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "append log duration per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Raft IO", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 5, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_raft_ready_handled_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_raftstore_raft_ready_handled_total", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(rate(tikv_raftstore_raft_process_duration_secs_count{type=\"ready\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "count", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "ready handled", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 118, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_raft_process_duration_secs_bucket{type='ready'}[1m])) by (le, job))", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "C", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 1 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "process ready duration per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1165, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_raft_process_duration_secs_bucket{type='tick'}[1m])) by (le, job))", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "C", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 1 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "process tick duration per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Raft Process", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1615, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_raft_sent_message_total[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "sent messages per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1616, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_server_raft_message_flush_total[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_server_raft_message_flush_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "flush messages per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 106, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_server_raft_message_recv_total[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "recv messages per server", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 11, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_raft_sent_message_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "messages", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 25, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_raft_sent_message_total{type=\"vote\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "vote", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1309, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_raft_dropped_message_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "raft dropped messages", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Raft Message", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 108, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_apply_proposal_bucket[1m])) by (le, job))", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "raft proposals per ready", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 7, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_proposal_total{type=~\"local_read|normal|read_index\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_raftstore_proposal_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "raft read/write proposals", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 119, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_proposal_total{type=~\"local_read|read_index\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "raft read proposals per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 120, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_proposal_total{type=\"normal\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_raftstore_proposal_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "raft write proposals per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 41, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_request_wait_time_duration_secs_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "tikv_raftstore_request_wait_time_duration_secs_bucket", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_request_wait_time_duration_secs_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_raftstore_request_wait_time_duration_secs_sum[1m])) / sum(rate(tikv_raftstore_request_wait_time_duration_secs_count[1m]))", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "propose wait duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 42, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_request_wait_time_duration_secs_bucket[1m])) by (le, job))", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "propose wait duration per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 1975, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": true, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(rate(tikv_raftstore_propose_log_size_sum[1m])) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "raft log speed", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "short", + "label": "bytes/s", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Raft Propose", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 76, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_proposal_total{type=~\"conf_change|transfer_leader\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_raftstore_proposal_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "admin proposals", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 77, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_admin_cmd_total{status=\"success\", type!=\"compact\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_raftstore_admin_cmd_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "admin apply", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 70, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_check_split_total{type!=\"ignore\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_raftstore_check_split_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "check split", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 71, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tikv_raftstore_check_split_duration_seconds_bucket[1m])) by (le, job))", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_raftstore_check_split_duration_seconds_bucket", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99.99% check split duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Raft Admin", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 2, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_storage_command_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "storage command total", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 8, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_storage_engine_async_request_total{status!~\"all|success\"}[1m])) by (status)", + "intervalFactor": 2, + "legendFormat": "{{status}}", + "metric": "tikv_raftstore_raft_process_duration_secs_bucket", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "storage async request error", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 15, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{type=\"snapshot\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{type=\"snapshot\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_engine_async_request_duration_seconds_sum{type=\"snapshot\"}[1m])) / sum(rate(tikv_storage_engine_async_request_duration_seconds_count{type=\"snapshot\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "storage async snapshot duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 109, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{type=\"write\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{type=\"write\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_engine_async_request_duration_seconds_sum{type=\"write\"}[1m])) / sum(rate(tikv_storage_engine_async_request_duration_seconds_count{type=\"write\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "storage async write duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1310, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "raft-95%", + "yaxis": 2 + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_storage_batch_commands_total_bucket[30s])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_storage_batch_commands_total_bucket[30s])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_batch_commands_total_sum[30s])) / sum(rate(tikv_storage_batch_commands_total_count[30s]))", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_batch_snapshot_commands_total_bucket[30s])) by (le))", + "intervalFactor": 2, + "legendFormat": "raft-95%", + "refId": "D", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "storage async batch snapshot", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "Storage Batch Size", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": "Raftstore Batch Size", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Storage", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "height": "400", + "id": 167, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_too_busy_total[1m])) by (stage)", + "intervalFactor": 2, + "legendFormat": "busy", + "refId": "A", + "step": 20 + }, + { + "expr": "sum(rate(tikv_scheduler_stage_total[1m])) by (stage)", + "intervalFactor": 2, + "legendFormat": "{{stage}}", + "refId": "B", + "step": 20 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler stage total", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "height": "", + "id": 1, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": 6, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_commands_pri_total[1m])) by (priority)", + "intervalFactor": 2, + "legendFormat": "{{priority}}", + "metric": "", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler priority commands", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 300 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "120s", + "handler": 1, + "message": "TiKV scheduler context total", + "name": "scheduler pending commands alert", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "height": "", + "id": 193, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": 6, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_scheduler_contex_total) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "", + "refId": "A", + "step": 40 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 300 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "scheduler pending commands", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Scheduler", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "height": "400", + "id": 168, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_too_busy_total{type=\"$command\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "busy", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(rate(tikv_scheduler_stage_total{type=\"$command\"}[1m])) by (stage)", + "intervalFactor": 2, + "legendFormat": "{{stage}}", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler stage total", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 3, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_scheduler_command_duration_seconds_bucket{type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_scheduler_command_duration_seconds_bucket{type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_scheduler_command_duration_seconds_sum{type=\"$command\"}[1m])) / sum(rate(tikv_scheduler_command_duration_seconds_count{type=\"$command\"}[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler command duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 194, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_scheduler_latch_wait_duration_seconds_bucket{type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_scheduler_latch_wait_duration_seconds_bucket{type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_scheduler_latch_wait_duration_seconds_sum{type=\"$command\"}[1m])) / sum(rate(tikv_scheduler_latch_wait_duration_seconds_count{type=\"$command\"}[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler latch wait duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 195, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_scheduler_kv_command_key_read_bucket{type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "kv_command_key", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_scheduler_kv_command_key_read_bucket{type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_scheduler_kv_command_key_read_sum{type=\"$command\"}[1m])) / sum(rate(tikv_scheduler_kv_command_key_read_count{type=\"$command\"}[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler keys read", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 373, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_scheduler_kv_command_key_write_bucket{type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "kv_command_key", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_scheduler_kv_command_key_write_bucket{type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_scheduler_kv_command_key_write_sum{type=\"$command\"}[1m])) / sum(rate(tikv_scheduler_kv_command_key_write_count{type=\"$command\"}[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler keys written", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 560, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_kv_scan_details{req=\"$command\"}[1m])) by (tag)", + "intervalFactor": 2, + "legendFormat": "{{tag}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler scan details", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 675, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_kv_scan_details{req=\"$command\", cf=\"lock\"}[1m])) by (tag)", + "intervalFactor": 2, + "legendFormat": "{{tag}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler scan details [lock]", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 829, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_kv_scan_details{req=\"$command\", cf=\"write\"}[1m])) by (tag)", + "intervalFactor": 2, + "legendFormat": "{{tag}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler scan details [write]", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 830, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_kv_scan_details{req=\"$command\", cf=\"default\"}[1m])) by (tag)", + "intervalFactor": 2, + "legendFormat": "{{tag}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler scan details [default]", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": "command", + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Scheduler - $command", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 5, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 16, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_coprocessor_request_duration_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_duration_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-95%", + "refId": "B", + "step": 4 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_duration_seconds_sum{req=\"select\"}[1m])) / sum(rate(tikv_coprocessor_request_duration_seconds_count{req=\"select\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "select-avg", + "refId": "C", + "step": 4 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_duration_seconds_sum{req=\"index\"}[1m])) / sum(rate(tikv_coprocessor_request_duration_seconds_count{req=\"index\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "index-avg", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor request duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 5, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 111, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_coprocessor_request_wait_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_wait_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-95%", + "refId": "B", + "step": 4 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_wait_seconds_sum[1m])) / sum(rate(tikv_coprocessor_request_wait_seconds_count[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor wait duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 5, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 113, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_coprocessor_request_handle_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_handle_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-95%", + "refId": "B", + "step": 4 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_handle_seconds_sum{req=\"select\"}[1m])) / sum(rate(tikv_coprocessor_request_handle_seconds_count{req=\"select\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "select-avg", + "refId": "C", + "step": 4 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_handle_seconds_sum{req=\"index\"}[1m])) / sum(rate(tikv_coprocessor_request_handle_seconds_count{req=\"index\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "index-avg", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor handle duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 5, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 115, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_duration_seconds_bucket[1m])) by (le, job,req))", + "intervalFactor": 2, + "legendFormat": "{{job}}-{{req}}", + "metric": "tikv_coprocessor_request_duration_seconds_bucket", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "95% coprocessor request duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 5, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 116, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_wait_seconds_bucket[1m])) by (le, job,req))", + "intervalFactor": 2, + "legendFormat": "{{job}}-{{req}}", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "95% coprocessor wait duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 5, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 117, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_handle_seconds_bucket[1m])) by (le, job,req))", + "intervalFactor": 2, + "legendFormat": "{{job}}-{{req}}", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "95% coprocessor handle duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 52, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, avg(rate(tikv_coprocessor_scan_keys_bucket[1m])) by (le, req)) ", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{req}}-99%", + "metric": "tikv_coprocessor_scan_keys_bucket", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, avg(rate(tikv_coprocessor_scan_keys_bucket[1m])) by (le, req)) ", + "intervalFactor": 2, + "legendFormat": "{{req}}-95%", + "metric": "tikv_coprocessor_scan_keys_bucket", + "refId": "B", + "step": 10 + }, + { + "expr": "histogram_quantile(0.90, avg(rate(tikv_coprocessor_scan_keys_bucket[1m])) by (le, req)) ", + "intervalFactor": 2, + "legendFormat": "{{req}}-90%", + "metric": "tikv_coprocessor_scan_keys_bucket", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor scan keys", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 551, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_executor_count[1m])) by (type)", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor executor count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 74, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_request_error[1m])) by (reason)", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{reason}}", + "metric": "tikv_coprocessor_request_error", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor request errors", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 552, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_scan_details[1m])) by (tag,req)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{req}}-{{tag}}", + "metric": "scan_details", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor scan details", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 122, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_scan_details{req=\"select\"}[1m])) by (tag,cf)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{cf}}-{{tag}}", + "metric": "scan_details", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor table scan details -- cf", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 554, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": "cf", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_scan_details{req=\"index\"}[1m])) by (tag,cf)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{cf}}-{{tag}}", + "metric": "scan_details", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor index scan details - cf", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Coprocessor", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 26, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1.0, sum(rate(tikv_storage_mvcc_versions_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " max", + "metric": "", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_storage_mvcc_versions_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "B", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_storage_mvcc_versions_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " 95%", + "metric": "", + "refId": "C", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_mvcc_versions_sum[1m])) / sum(rate(tikv_storage_mvcc_versions_count[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "D", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "MVCC Versions", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 559, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1.0, sum(rate(tikv_storage_mvcc_gc_delete_versions_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " max", + "metric": "", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_storage_mvcc_gc_delete_versions_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "B", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_storage_mvcc_gc_delete_versions_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " 95%", + "metric": "", + "refId": "C", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_mvcc_gc_delete_versions_sum[1m])) / sum(rate(tikv_storage_mvcc_gc_delete_versions_count[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "D", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "MVCC Delete Versions", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 121, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_storage_command_total{type=\"gc\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "total", + "metric": "tikv_storage_command_total", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_gc_skipped_counter[1m]))", + "intervalFactor": 2, + "legendFormat": "skipped", + "metric": "tikv_storage_gc_skipped_counter", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GC Commands", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 967, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_gc_action_result_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GC Actions Result", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 966, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_gc_worker_actions_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GC Worker Actions", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 969, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1.0, sum(rate(tidb_tikvclient_gc_seconds_bucket[1m])) by (instance, le))", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "gc Seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 968, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_gc_failure_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "gc failure", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 0, + "editable": true, + "error": false, + "format": "s", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 27, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 3, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "max(tidb_tikvclient_gc_config{type=\"tikv_gc_life_time\"})", + "interval": "", + "intervalFactor": 2, + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "GC LifeTime", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 0, + "editable": true, + "error": false, + "format": "s", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 28, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 3, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "targets": [ + { + "expr": "max(tidb_tikvclient_gc_config{type=\"tikv_gc_run_interval\"})", + "intervalFactor": 2, + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "GC interval", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "GC", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 35, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(tikv_raftstore_raft_sent_message_total{type=\"snapshot\"}[1m]))", + "intervalFactor": 2, + "legendFormat": " ", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "rate snapshot message", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 36, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_server_send_snapshot_duration_seconds_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "send", + "refId": "A", + "step": 60 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_snapshot_duration_seconds_bucket{type=\"apply\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "apply", + "refId": "B", + "step": 60 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_snapshot_duration_seconds_bucket{type=\"generate\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "generate", + "refId": "C", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% handle snapshot duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 38, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 4, + "stack": false, + "steppedLine": true, + "targets": [ + { + "expr": "sum(tikv_raftstore_snapshot_traffic_total) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "snapshot state count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 44, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tikv_snapshot_size_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "size", + "metric": "tikv_snapshot_size_bucket", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99.99% snapshot size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 43, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tikv_snapshot_kv_count_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "count", + "metric": "tikv_snapshot_kv_count_bucket", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99.99% snapshot kv count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Snapshot", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 59, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 400, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_worker_handled_task_total[1m])) by (name)", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Worker Handled Tasks", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1395, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 400, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_worker_pending_task_total[1m])) by (name)", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Worker Pending Tasks", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1876, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 400, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_futurepool_handled_task_total[1m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "FuturePool Handled Tasks", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1877, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 400, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_futurepool_pending_task_total[1m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "FuturePool Pending Tasks", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 2 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "10s", + "handler": 1, + "message": "coprocessor pending requests", + "name": "coprocessor pending requests alert", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 550, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_pending_request[1m])) by (req, priority)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{ req }} - {{priority}}", + "metric": "tikv_coprocessor_request_error", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(rate(tikv_coprocessor_pending_request[1m])) BY (type, instance)", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 2 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor pending requests", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Task", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 0.8 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "datasourceId": 1, + "model": { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"raftstore_.*\"}[1m])) by (job, name)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 20 + }, + "params": [ + "A", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "60s", + "handler": 1, + "message": "TiKV raftstore thread CPU usage is high", + "name": "TiKV raft store CPU alert", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 61, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"raftstore_.*\"}[1m])) by (job, name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0.8 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "raft store CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 79, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=\"apply_worker\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "async apply CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 63, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"storage_schedul.*\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 64, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"sched_.*\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler Worker CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1908, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"store_read.*\"}[1m])) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Storage ReadPool CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 78, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"cop_.*\"}[1m])) by (job)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Coprocessor CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 67, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"snapshot_worker.*\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "snapshot worker CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 68, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"split_check.*\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "split check CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 69, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "max(rate(tikv_thread_cpu_seconds_total{name=~\"rocksdb.*\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "warning", + "fill": true, + "line": true, + "op": "gt", + "value": 1 + }, + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 4 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "rocksdb CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 105, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"grpc.*\"}[1m])) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "grpc poll CPU", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Thread CPU", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 138, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_memtable_efficiency{db=\"$db\", type=\"memtable_hit\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "memtable", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=~\"block_cache_data_hit|block_cache_filter_hit\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "block_cache", + "metric": "", + "refId": "E", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_get_served{db=\"$db\", type=\"get_hit_l0\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "l0", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_get_served{db=\"$db\", type=\"get_hit_l1\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "l1", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_get_served{db=\"$db\", type=\"get_hit_l2_and_up\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "l2_and_up", + "refId": "F", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Get Operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 82, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_get_micro_seconds{db=\"$db\",type=\"get_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_get_micro_seconds{db=\"$db\",type=\"get_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_get_micro_seconds{db=\"$db\",type=\"get_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_get_micro_seconds{db=\"$db\",type=\"get_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Get Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 129, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_locate{db=\"$db\", type=\"number_db_seek\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "seek", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_locate{db=\"$db\", type=\"number_db_seek_found\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "seek_found", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_locate{db=\"$db\", type=\"number_db_next\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "next", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_locate{db=\"$db\", type=\"number_db_next_found\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "next_found", + "metric": "", + "refId": "D", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_locate{db=\"$db\", type=\"number_db_prev\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "prev", + "metric": "", + "refId": "E", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_locate{db=\"$db\", type=\"number_db_prev_found\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "prev_found", + "metric": "", + "refId": "F", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Seek Operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 125, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_seek_micro_seconds{db=\"$db\",type=\"seek_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_seek_micro_seconds{db=\"$db\",type=\"seek_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_seek_micro_seconds{db=\"$db\",type=\"seek_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_seek_micro_seconds{db=\"$db\",type=\"seek_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Seek Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 139, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_write_served{db=\"$db\", type=~\"write_done_by_self|write_done_by_other\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "done", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_write_served{db=\"$db\", type=\"write_timeout\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "timeout", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_write_served{db=\"$db\", type=\"write_with_wal\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "with_wal", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Write Operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 126, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_write_micro_seconds{db=\"$db\",type=\"write_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_micro_seconds{db=\"$db\",type=\"write_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_micro_seconds{db=\"$db\",type=\"write_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_micro_seconds{db=\"$db\",type=\"write_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Write Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 137, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_wal_file_synced{db=\"$db\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "sync", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "WAL Sync Operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 135, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 6, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_wal_file_sync_micro_seconds{db=\"$db\",type=\"wal_file_sync_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_wal_file_sync_micro_seconds{db=\"$db\",type=\"wal_file_sync_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_wal_file_sync_micro_seconds{db=\"$db\",type=\"wal_file_sync_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_wal_file_sync_micro_seconds{db=\"$db\",type=\"wal_file_sync_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "WAL Sync Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 128, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_event_total{db=\"$db\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_engine_event_total", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compaction Operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 136, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_compaction_time{db=\"$db\",type=\"compaction_time_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_compaction_time{db=\"$db\",type=\"compaction_time_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_compaction_time{db=\"$db\",type=\"compaction_time_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_compaction_time{db=\"$db\",type=\"compaction_time_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compaction Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 140, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_sst_read_micros{db=\"$db\", type=\"sst_read_micros_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_sst_read_micros{db=\"$db\", type=\"sst_read_micros_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_sst_read_micros{db=\"$db\", type=\"sst_read_micros_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_sst_read_micros{db=\"$db\", type=\"sst_read_micros_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "SST Read Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 10, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 87, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_write_stall{db=\"$db\", type=\"write_stall_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_stall{db=\"$db\", type=\"write_stall_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_stall{db=\"$db\", type=\"write_stall_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_stall{db=\"$db\", type=\"write_stall_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Write Stall Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 10, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 103, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_memory_bytes{db=\"$db\", type=\"mem-tables\"}) by (cf)", + "intervalFactor": 2, + "legendFormat": "{{cf}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memtable Size", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 88, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": null, + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_memtable_efficiency{db=\"$db\", type=\"memtable_hit\"}[1m])) / (sum(rate(tikv_engine_memtable_efficiency{db=\"$db\", type=\"memtable_hit\"}[1m])) + sum(rate(tikv_engine_memtable_efficiency{db=\"$db\", type=\"memtable_miss\"}[1m])))", + "intervalFactor": 2, + "legendFormat": "hit", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memtable Hit", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 102, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_block_cache_size_bytes{db=\"$db\"}) by(cf)", + "intervalFactor": 2, + "legendFormat": "{{cf}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Block Cache Size", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 80, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 6, + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_hit\"}[1m])) / (sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_hit\"}[1m])) + sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_miss\"}[1m])))", + "intervalFactor": 2, + "legendFormat": "all", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_data_hit\"}[1m])) / (sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_data_hit\"}[1m])) + sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_data_miss\"}[1m])))", + "intervalFactor": 2, + "legendFormat": "data", + "metric": "", + "refId": "D", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_filter_hit\"}[1m])) / (sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_filter_hit\"}[1m])) + sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_filter_miss\"}[1m])))", + "intervalFactor": 2, + "legendFormat": "filter", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_index_hit\"}[1m])) / (sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_index_hit\"}[1m])) + sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_index_miss\"}[1m])))", + "intervalFactor": 2, + "legendFormat": "index", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_bloom_efficiency{db=\"$db\", type=\"bloom_prefix_useful\"}[1m])) / sum(rate(tikv_engine_bloom_efficiency{db=\"$db\", type=\"bloom_prefix_checked\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "bloom prefix", + "metric": "", + "refId": "E", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Block Cache Hit", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "height": "", + "id": 467, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"block_cache_byte_read\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "total_read", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"block_cache_byte_write\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "total_written", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_data_bytes_insert\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "data_insert", + "metric": "", + "refId": "D", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_filter_bytes_insert\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "filter_insert", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_filter_bytes_evict\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "filter_evict", + "metric": "", + "refId": "E", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_index_bytes_insert\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "index_insert", + "metric": "", + "refId": "F", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_index_bytes_evict\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "index_evict", + "metric": "", + "refId": "G", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Block Cache Flow", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 468, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_add\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "total_add", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_data_add\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "data_add", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_filter_add\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "filter_add", + "metric": "", + "refId": "D", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_index_add\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "index_add", + "metric": "", + "refId": "E", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_add_failures\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "add_failures", + "metric": "", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Block Cache Operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "height": "", + "id": 132, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"keys_read\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "read", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"keys_written\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "written", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_compaction_num_corrupt_keys{db=\"$db\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "corrupt", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Keys Flow", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 131, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_estimate_num_keys{db=\"$db\"}) by (cf)", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{cf}}", + "metric": "tikv_engine_estimate_num_keys", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Total Keys", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "height": "", + "id": 85, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"bytes_read\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "get", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"iter_bytes_read\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "scan", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Read Flow", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 133, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 6, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_bytes_per_read{db=\"$db\",type=\"bytes_per_read_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_read{db=\"$db\",type=\"bytes_per_read_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_read{db=\"$db\",type=\"bytes_per_read_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_read{db=\"$db\",type=\"bytes_per_read_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Bytes / Read", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "height": "", + "id": 86, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"wal_file_bytes\"}[1m]))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "wal", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"bytes_written\"}[1m]))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "write", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Write Flow", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 134, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 6, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_bytes_per_write{db=\"$db\",type=\"bytes_per_write_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_write{db=\"$db\",type=\"bytes_per_write_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_write{db=\"$db\",type=\"bytes_per_write_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_write{db=\"$db\",type=\"bytes_per_write_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Bytes / Write", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 90, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_compaction_flow_bytes{db=\"$db\", type=\"bytes_read\"}[1m]))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "read", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_compaction_flow_bytes{db=\"$db\", type=\"bytes_written\"}[1m]))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "written", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"flush_write_bytes\"}[1m]))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "flushed", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compaction Flow", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 127, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_pending_compaction_bytes{db=\"$db\"}[1m])) by (cf)", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{cf}}", + "metric": "tikv_engine_pending_compaction_bytes", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compaction Pending Bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 518, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_read_amp_flow_bytes{db=\"$db\", type=\"read_amp_total_read_bytes\"}[1m])) by (job) / sum(rate(tikv_engine_read_amp_flow_bytes{db=\"$db\", type=\"read_amp_estimate_useful_bytes\"}[1m])) by (job)", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Read Amplication", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 863, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_compression_ratio{db=\"$db\"}) by (level)", + "hide": false, + "intervalFactor": 2, + "legendFormat": "level - {{level}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compression Ratio", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 516, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "tikv_engine_num_snapshots{db=\"$db\"}", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Number of Snapshots", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 517, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "tikv_engine_oldest_snapshot_duration{db=\"$db\"}", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_engine_oldest_snapshot_duration", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Oldest Snapshots Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 2002, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": true, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_num_files_at_level{db=\"$db\"}) by (cf, level)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "cf-{{cf}}, level-{{level}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Number files at each level", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 2003, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": true, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_snapshot_ingest_sst_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A" + }, + { + "expr": "sum(rate(tikv_snapshot_ingest_sst_duration_seconds_sum[1m])) / sum(rate(tikv_snapshot_ingest_sst_duration_seconds_count[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "average", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Ingest SST duration seconds", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": "db", + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Rocksdb - $db", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 95, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_duration_seconds_count{type!=\"kv_gc\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_grpc_msg_duration_seconds_bucket", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "grpc message count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 107, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_fail_total{type!=\"kv_gc\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_grpc_msg_fail_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "grpc message failed", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 98, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_grpc_msg_duration_seconds_bucket{type!=\"kv_gc\"}[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% grpc messge duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 10, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 1844, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_duration_seconds_count{type=\"kv_gc\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "gc message cnt", + "metric": "tikv_grpc_msg_duration_seconds_bucket", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_grpc_msg_fail_total{type=\"kv_gc\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "gc failed cnt", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "grpc gc message count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 1845, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_grpc_msg_duration_seconds_bucket{type=\"kv_gc\"}[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% grpc kv_gc messge duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 10, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Grpc", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 1069, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 350, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_pd_request_duration_seconds_count[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{ type }}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 1070, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 350, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_pd_request_duration_seconds_sum[1m])) by (type) / sum(rate(tikv_pd_request_duration_seconds_count[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{ type }}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD request duration (average)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 1215, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 350, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_pd_heartbeat_message_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{ type }}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD heartbeats", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 1396, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 350, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_pd_validate_peer_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{ type }}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD validate peers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "PD", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "${DS_TEST-CLUSTER}", + "hide": 0, + "includeAll": true, + "label": "db", + "multi": true, + "name": "db", + "options": [], + "query": "label_values(tikv_engine_block_cache_size_bytes, db)", + "refresh": 1, + "regex": "", + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_TEST-CLUSTER}", + "hide": 0, + "includeAll": true, + "label": "command", + "multi": true, + "name": "command", + "options": [], + "query": "label_values(tikv_storage_command_total, type)", + "refresh": 1, + "regex": "", + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "Test-Cluster-TiKV", + "version": 2 +} diff --git a/docker/tidb/config/tikv.rules.yml b/docker/tidb/config/tikv.rules.yml new file mode 100755 index 0000000..3b08f94 --- /dev/null +++ b/docker/tidb/config/tikv.rules.yml @@ -0,0 +1,350 @@ +groups: +- name: alert.rules + rules: + - alert: TiKV_memory_used_too_fast + expr: process_resident_memory_bytes{job=~"tikv.*"} - (process_resident_memory_bytes{job=~"tikv.*"} offset 5m) > 5*1024*1024*1024 + for: 5m + labels: + env: test-cluster + level: emergency + expr: process_resident_memory_bytes{job=~"tikv.*"} - (process_resident_memory_bytes{job=~"tikv.*"} offset 5m) > 5*1024*1024*1024 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, job: {{ $labels.job }}, values: {{ $value }}' + value: '{{ $value }}' + summary: TiKV memory used too fast + + - alert: TiKV_GC_can_not_work + expr: sum(increase(tidb_tikvclient_gc_action_result{type="success"}[6h])) < 1 + for: 1m + labels: + env: test-cluster + level: emergency + expr: sum(increase(tidb_tikvclient_gc_action_result{type="success"}[6h])) < 1 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiKV GC can not work + + - alert: TiKV_server_report_failure_msg_total + expr: sum(rate(tikv_server_report_failure_msg_total{type="unreachable"}[10m])) BY (store_id) > 10 + for: 1m + labels: + env: test-cluster + level: critical + expr: sum(rate(tikv_server_report_failure_msg_total{type="unreachable"}[10m])) BY (store_id) > 10 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiKV server_report_failure_msg_total error + + - alert: TiKV_channel_full_total + expr: sum(rate(tikv_channel_full_total[10m])) BY (type, instance) > 0 + for: 1m + labels: + env: test-cluster + level: critical + expr: sum(rate(tikv_channel_full_total[10m])) BY (type, instance) > 0 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiKV channel full + + - alert: TiKV_write_stall + expr: delta( tikv_engine_write_stall[10m]) > 0 + for: 1m + labels: + env: test-cluster + level: critical + expr: delta( tikv_engine_write_stall[10m]) > 0 + annotations: + description: 'cluster: test-cluster, type: {{ $labels.type }}, instance: {{ $labels.instance }}, values: {{ $value }}' + value: '{{ $value }}' + summary: TiKV write stall + + - alert: TiKV_raft_log_lag + expr: histogram_quantile(0.99, sum(rate(tikv_raftstore_log_lag_bucket[1m])) by (le, instance, job)) > 5000 + for: 1m + labels: + env: test-cluster + level: critical + expr: histogram_quantile(0.99, sum(rate(tikv_raftstore_log_lag_bucket[1m])) by (le, instance, job)) > 5000 + annotations: + description: 'cluster: test-cluster, instance {{ $labels.instance }}, values: {{ $value }}' + value: '{{ $value }}' + summary: TiKV raftstore log lag more than 5000 + + - alert: TiKV_async_request_snapshot_duration_seconds + expr: histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{type="snapshot"}[1m])) by (le, instance, job,type)) > 1 + for: 1m + labels: + env: test-cluster + level: critical + expr: histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{type="snapshot"}[1m])) by (le, instance, job,type)) > 1 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiKV async request snapshot duration seconds more than 1s + + - alert: TiKV_async_request_write_duration_seconds + expr: histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{type="write"}[1m])) by (le, instance, job,type)) > 1 + for: 1m + labels: + env: test-cluster + level: critical + expr: histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{type="write"}[1m])) by (le, instance, job,type)) > 1 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiKV async request write duration seconds more than 1s + + - alert: TiKV_coprocessor_request_wait_seconds + expr: histogram_quantile(0.9999, sum(rate(tikv_coprocessor_request_wait_seconds_bucket[1m])) by (le, instance, job,req)) > 10 + for: 1m + labels: + env: test-cluster + level: critical + expr: histogram_quantile(0.9999, sum(rate(tikv_coprocessor_request_wait_seconds_bucket[1m])) by (le, instance, job,req)) > 10 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiKV coprocessor request wait seconds more than 10s + + - alert: TiKV_raftstore_thread_cpu_seconds_total + expr: sum(rate(tikv_thread_cpu_seconds_total{name=~"raftstore_.*"}[1m])) by (job, name) > 0.8 + for: 1m + labels: + env: test-cluster + level: critical + expr: sum(rate(tikv_thread_cpu_seconds_total{name=~"raftstore_.*"}[1m])) by (job, name) > 0.8 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiKV raftstore thread CPU seconds is high + + - alert: TiKV_raft_append_log_duration_secs + expr: histogram_quantile(0.99, sum(rate(tikv_raftstore_append_log_duration_seconds_bucket[1m])) by (le, instance, job)) > 1 + for: 1m + labels: + env: test-cluster + level: critical + expr: histogram_quantile(0.99, sum(rate(tikv_raftstore_append_log_duration_seconds_bucket[1m])) by (le, instance, job)) > 1 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiKV_raft_append_log_duration_secs + + - alert: TiKV_raft_apply_log_duration_secs + expr: histogram_quantile(0.99, sum(rate(tikv_raftstore_apply_log_duration_seconds_bucket[1m])) by (le, instance, job)) > 1 + for: 1m + labels: + env: test-cluster + level: critical + expr: histogram_quantile(0.99, sum(rate(tikv_raftstore_apply_log_duration_seconds_bucket[1m])) by (le, instance, job)) > 1 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiKV_raft_apply_log_duration_secs + + - alert: TiKV_scheduler_latch_wait_duration_seconds + expr: histogram_quantile(0.99, sum(rate(tikv_scheduler_latch_wait_duration_seconds_bucket[1m])) by (le, instance, job,type)) > 1 + for: 1m + labels: + env: test-cluster + level: critical + expr: histogram_quantile(0.99, sum(rate(tikv_scheduler_latch_wait_duration_seconds_bucket[1m])) by (le, instance, job,type)) > 1 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiKV scheduler latch wait duration seconds more than 1s + + - alert: TiKV_thread_apply_worker_cpu_seconds + expr: sum(rate(tikv_thread_cpu_seconds_total{name="apply_worker"}[1m])) by (job) > 0.9 + for: 1m + labels: + env: test-cluster + level: critical + expr: sum(rate(tikv_thread_cpu_seconds_total{name="apply_worker"}[1m])) by (job) > 0.9 + annotations: + description: 'cluster: test-cluster, type: {{ $labels.type }}, instance: {{ $labels.instance }}, values: {{ $value }}' + value: '{{ $value }}' + summary: TiKV thread apply worker cpu seconds is high + + - alert: TiDB_tikvclient_gc_action_fail + expr: sum(increase(tidb_tikvclient_gc_action_result{type="fail"}[1m])) > 10 + for: 1m + labels: + env: test-cluster + level: critical + expr: sum(increase(tidb_tikvclient_gc_action_result{type="fail"}[1m])) > 10 + annotations: + description: 'cluster: test-cluster, type: {{ $labels.type }}, instance: {{ $labels.instance }}, values: {{ $value }}' + value: '{{ $value }}' + summary: TiDB_tikvclient_gc_action_fail + + - alert: TiKV_leader_drops + expr: delta(tikv_pd_heartbeat_tick_total{type="leader"}[30s]) < -10 + for: 1m + labels: + env: test-cluster + level: warning + expr: delta(tikv_pd_heartbeat_tick_total{type="leader"}[30s]) < -10 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiKV leader drops + + - alert: TiKV_raft_process_ready_duration_secs + expr: histogram_quantile(0.999, sum(rate(tikv_raftstore_raft_process_duration_secs_bucket{type='ready'}[1m])) by (le, instance, job,type)) > 2 + for: 1m + labels: + env: test-cluster + level: warning + expr: histogram_quantile(0.999, sum(rate(tikv_raftstore_raft_process_duration_secs_bucket{type='ready'}[1m])) by (le, instance, job,type)) > 2 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values: {{ $value }}' + value: '{{ $value }}' + summary: TiKV_raft_process_ready_duration_secs + + - alert: TiKV_raft_process_tick_duration_secs + expr: histogram_quantile(0.999, sum(rate(tikv_raftstore_raft_process_duration_secs_bucket{type='tick'}[1m])) by (le, instance, job,type)) > 2 + for: 1m + labels: + env: test-cluster + level: warning + expr: histogram_quantile(0.999, sum(rate(tikv_raftstore_raft_process_duration_secs_bucket{type='tick'}[1m])) by (le, instance, job,type)) > 2 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values: {{ $value }}' + value: '{{ $value }}' + summary: TiKV_raft_process_tick_duration_secs + + - alert: TiKV_scheduler_context_total + expr: abs(delta( tikv_scheduler_contex_total[5m])) > 1000 + for: 1m + labels: + env: test-cluster + level: warning + expr: abs(delta( tikv_scheduler_contex_total[5m])) > 1000 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiKV scheduler context total + + - alert: TiKV_scheduler_command_duration_seconds + expr: histogram_quantile(0.99, sum(rate(tikv_scheduler_command_duration_seconds_bucket[1m])) by (le, instance, job,type) / 1000) > 1 + for: 1m + labels: + env: test-cluster + level: warning + expr: histogram_quantile(0.99, sum(rate(tikv_scheduler_command_duration_seconds_bucket[1m])) by (le, instance, job,type) / 1000) > 1 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiKV scheduler command duration seconds more than 1s + + - alert: TiKV_thread_storage_scheduler_cpu_seconds + expr: sum(rate(tikv_thread_cpu_seconds_total{name=~"storage_schedul.*"}[1m])) by (job) > 0.8 + for: 1m + labels: + env: test-cluster + level: warning + expr: sum(rate(tikv_thread_cpu_seconds_total{name=~"storage_schedul.*"}[1m])) by (job) > 0.8 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values:{{ $value }}' + value: '{{ $value }}' + summary: TiKV storage scheduler cpu seconds more than 80% + + - alert: TiKV_coprocessor_outdated_request_wait_seconds + expr: delta( tikv_coprocessor_outdated_request_wait_seconds_count[10m] ) > 0 + for: 1m + labels: + env: test-cluster + level: warning + expr: delta( tikv_coprocessor_outdated_request_wait_seconds_count[10m] ) > 0 + annotations: + description: 'cluster: test-cluster, instance: {{ $labels.instance }}, values: {{ $value }}' + value: '{{ $value }}' + summary: TiKV coprocessor outdated request wait seconds + + - alert: TiKV_coprocessor_request_error + expr: increase(tikv_coprocessor_request_error{reason!="lock"}[10m]) > 100 + for: 1m + labels: + env: test-cluster + level: warning + expr: increase(tikv_coprocessor_request_error{reason!="lock"}[10m]) > 100 + annotations: + description: 'cluster: test-cluster, reason: {{ $labels.reason }}, instance: {{ $labels.instance }}, values: {{ $value }}' + value: '{{ $value }}' + summary: TiKV coprocessor request error + + - alert: TiKV_coprocessor_request_lock_error + expr: increase(tikv_coprocessor_request_error{reason="lock"}[10m]) > 10000 + for: 1m + labels: + env: test-cluster + level: warning + expr: increase(tikv_coprocessor_request_error{reason="lock"}[10m]) > 10000 + annotations: + description: 'cluster: test-cluster, reason: {{ $labels.reason }}, instance: {{ $labels.instance }}, values: {{ $value }}' + value: '{{ $value }}' + summary: TiKV coprocessor request lock error + + - alert: TiKV_coprocessor_pending_request + expr: delta( tikv_coprocessor_pending_request[10m]) > 5000 + for: 1m + labels: + env: test-cluster + level: warning + expr: delta( tikv_coprocessor_pending_request[10m]) > 5000 + annotations: + description: 'cluster: test-cluster, type: {{ $labels.type }}, instance: {{ $labels.instance }}, values: {{ $value }}' + value: '{{ $value }}' + summary: TiKV pending {{ $labels.type }} request is high + + - alert: TiKV_batch_request_snapshot_nums + expr: sum(rate(tikv_thread_cpu_seconds_total{name=~"endpoint.*"}[1m])) by (job) / ( count(tikv_thread_cpu_seconds_total{name=~"endpoint.*"}) * 0.9 ) / count(count(tikv_thread_cpu_seconds_total) by (instance)) > 0 + for: 1m + labels: + env: test-cluster + level: warning + expr: sum(rate(tikv_thread_cpu_seconds_total{name=~"endpoint.*"}[1m])) by (job) / ( count(tikv_thread_cpu_seconds_total{name=~"endpoint.*"}) * 0.9 ) / count(count(tikv_thread_cpu_seconds_total) by (instance)) > 0 + annotations: + description: 'cluster: test-cluster, type: {{ $labels.type }}, instance: {{ $labels.instance }}, values: {{ $value }}' + value: '{{ $value }}' + summary: TiKV batch request snapshot nums is high + + - alert: TiKV_pending_task + expr: sum(tikv_worker_pending_task_total) BY (job,instance,name) > 1000 + for: 1m + labels: + env: test-cluster + level: warning + expr: sum(tikv_worker_pending_task_total) BY (job,instance,name) > 1000 + annotations: + description: 'cluster: test-cluster, type: {{ $labels.type }}, instance: {{ $labels.instance }}, values: {{ $value }}' + value: '{{ $value }}' + summary: TiKV pending task too much + + - alert: TiKV_low_space_and_add_region + expr: count( (sum(tikv_store_size_bytes{type="available"}) by (job) / sum(tikv_store_size_bytes{type="capacity"}) by (job) < 0.2) and (sum(tikv_raftstore_snapshot_traffic_total{type="applying"}) by (job) > 0 ) ) > 0 + for: 1m + labels: + env: test-cluster + level: warning + expr: count( (sum(tikv_store_size_bytes{type="available"}) by (job) / sum(tikv_store_size_bytes{type="capacity"}) by (job) < 0.2) and (sum(tikv_raftstore_snapshot_traffic_total{type="applying"}) by (job) > 0 ) ) > 0 + annotations: + description: 'cluster: test-cluster, type: {{ $labels.type }}, instance: {{ $labels.instance }}, values: {{ $value }}' + value: '{{ $value }}' + summary: TiKV low_space and add_region + + - alert: TiKV_approximate_region_size + expr: histogram_quantile(0.99, sum(rate(tikv_raftstore_region_size_bucket[1m])) by (le)) > 1073741824 + for: 1m + labels: + env: test-cluster + level: warning + expr: histogram_quantile(0.99, sum(rate(tikv_raftstore_region_size_bucket[1m])) by (le)) > 1073741824 + annotations: + description: 'cluster: test-cluster, type: {{ $labels.type }}, instance: {{ $labels.instance }}, values: {{ $value }}' + value: '{{ $value }}' + summary: TiKV approximate region size is more than 1GB diff --git a/docker/tidb/config/tikv.toml b/docker/tidb/config/tikv.toml new file mode 100755 index 0000000..6606cd9 --- /dev/null +++ b/docker/tidb/config/tikv.toml @@ -0,0 +1,497 @@ +# TiKV config template +# Human-readable big numbers: +# File size(based on byte): KB, MB, GB, TB, PB +# e.g.: 1_048_576 = "1MB" +# Time(based on ms): ms, s, m, h +# e.g.: 78_000 = "1.3m" + +# log level: trace, debug, info, warn, error, off. +log-level = "error" +# file to store log, write to stderr if it's empty. +# log-file = "" + +[readpool.storage] +# size of thread pool for high-priority operations +# high-concurrency = 4 +# size of thread pool for normal-priority operations +# normal-concurrency = 4 +# size of thread pool for low-priority operations +# low-concurrency = 4 +# max running high-priority operations, reject if exceed +# max-tasks-high = 8000 +# max running normal-priority operations, reject if exceed +# max-tasks-normal = 8000 +# max running low-priority operations, reject if exceed +# max-tasks-low = 8000 +# size of stack size for each thread pool +# stack-size = "10MB" + +[readpool.coprocessor] +# Notice: if CPU_NUM > 8, default thread pool size for coprocessors +# will be set to CPU_NUM * 0.8. + +# high-concurrency = 8 +# normal-concurrency = 8 +# low-concurrency = 8 +# max-tasks-high = 16000 +# max-tasks-normal = 16000 +# max-tasks-low = 16000 +# stack-size = "10MB" + +[server] +# set listening address. +# addr = "127.0.0.1:20160" +# set advertise listening address for client communication, if not set, use addr instead. +# advertise-addr = "" +# notify capacity, 40960 is suitable for about 7000 regions. +# notify-capacity = 40960 +# maximum number of messages can be processed in one tick. +# messages-per-tick = 4096 + +# compression type for grpc channel, available values are no, deflate and gzip. +# grpc-compression-type = "no" +# size of thread pool for grpc server. +# grpc-concurrency = 4 +# The number of max concurrent streams/requests on a client connection. +# grpc-concurrent-stream = 1024 +# The number of connections with each tikv server to send raft messages. +# grpc-raft-conn-num = 10 +# Amount to read ahead on individual grpc streams. +# grpc-stream-initial-window-size = "2MB" + +# How many snapshots can be sent concurrently. +# concurrent-send-snap-limit = 32 +# How many snapshots can be recv concurrently. +# concurrent-recv-snap-limit = 32 + +# max count of tasks being handled, new tasks will be rejected. +# end-point-max-tasks = 2000 + +# max recursion level allowed when decoding dag expression +# end-point-recursion-limit = 1000 + +# max time to handle coprocessor request before timeout +# end-point-request-max-handle-duration = "60s" + +# the max bytes that snapshot can be written to disk in one second, +# should be set based on your disk performance +# snap-max-write-bytes-per-sec = "100MB" + +# set attributes about this server, e.g. { zone = "us-west-1", disk = "ssd" }. +# labels = {} + +[storage] +# set the path to rocksdb directory. +# data-dir = "/tmp/tikv/store" + +# notify capacity of scheduler's channel +# scheduler-notify-capacity = 10240 + +# maximum number of messages can be processed in one tick +# scheduler-messages-per-tick = 1024 + +# the number of slots in scheduler latches, concurrency control for write. +# scheduler-concurrency = 2048000 + +# scheduler's worker pool size, should increase it in heavy write cases, +# also should less than total cpu cores. +# scheduler-worker-pool-size = 4 + +# When the pending write bytes exceeds this threshold, +# the "scheduler too busy" error is displayed. +# scheduler-pending-write-threshold = "100MB" + +[pd] +# pd endpoints +# endpoints = [] + +[metric] +# the Prometheus client push interval. Setting the value to 0s stops Prometheus client from pushing. +# interval = "15s" +# the Prometheus pushgateway address. Leaving it empty stops Prometheus client from pushing. +address = "pushgateway:9091" +# the Prometheus client push job name. Note: A node id will automatically append, e.g., "tikv_1". +# job = "tikv" + +[raftstore] +# true (default value) for high reliability, this can prevent data loss when power failure. +# sync-log = true + +# set the path to raftdb directory, default value is data-dir/raft +# raftdb-path = "" + +# set store capacity, if no set, use disk capacity. +# capacity = 0 + +# notify capacity, 40960 is suitable for about 7000 regions. +# notify-capacity = 40960 + +# maximum number of messages can be processed in one tick. +# messages-per-tick = 4096 + +# Region heartbeat tick interval for reporting to pd. +# pd-heartbeat-tick-interval = "60s" +# Store heartbeat tick interval for reporting to pd. +# pd-store-heartbeat-tick-interval = "10s" + +# When region size changes exceeds region-split-check-diff, we should check +# whether the region should be split or not. +# region-split-check-diff = "6MB" + +# Interval to check region whether need to be split or not. +# split-region-check-tick-interval = "10s" + +# When raft entry exceed the max size, reject to propose the entry. +# raft-entry-max-size = "8MB" + +# Interval to gc unnecessary raft log. +# raft-log-gc-tick-interval = "10s" +# A threshold to gc stale raft log, must >= 1. +# raft-log-gc-threshold = 50 +# When entry count exceed this value, gc will be forced trigger. +# raft-log-gc-count-limit = 72000 +# When the approximate size of raft log entries exceed this value, gc will be forced trigger. +# It's recommanded to set it to 3/4 of region-split-size. +# raft-log-gc-size-limit = "72MB" + +# When a peer hasn't been active for max-peer-down-duration, +# we will consider this peer to be down and report it to pd. +# max-peer-down-duration = "5m" + +# Interval to check whether start manual compaction for a region, +# region-compact-check-interval = "5m" +# Number of regions for each time to check. +# region-compact-check-step = 100 +# The minimum number of delete tombstones to trigger manual compaction. +# region-compact-min-tombstones = 10000 +# Interval to check whether should start a manual compaction for lock column family, +# if written bytes reach lock-cf-compact-threshold for lock column family, will fire +# a manual compaction for lock column family. +# lock-cf-compact-interval = "10m" +# lock-cf-compact-bytes-threshold = "256MB" + +# Interval (s) to check region whether the data are consistent. +# consistency-check-interval = 0 + +# Use delete range to drop a large number of continuous keys. +# use-delete-range = false + +# delay time before deleting a stale peer +# clean-stale-peer-delay = "10m" + +# Interval to cleanup import sst files. +# cleanup-import-sst-interval = "10m" + +[coprocessor] +# When it is true, it will try to split a region with table prefix if +# that region crosses tables. It is recommended to turn off this option +# if there will be a large number of tables created. +# split-region-on-table = true +# When the region's size exceeds region-max-size, we will split the region +# into two which the left region's size will be region-split-size or a little +# bit smaller. +# region-max-size = "144MB" +# region-split-size = "96MB" + +[rocksdb] +# Maximum number of concurrent background jobs (compactions and flushes) +# max-background-jobs = 8 + +# This value represents the maximum number of threads that will concurrently perform a +# compaction job by breaking it into multiple, smaller ones that are run simultaneously. +# Default: 1 (i.e. no subcompactions) +# max-sub-compactions = 1 + +# Number of open files that can be used by the DB. You may need to +# increase this if your database has a large working set. Value -1 means +# files opened are always kept open. You can estimate number of files based +# on target_file_size_base and target_file_size_multiplier for level-based +# compaction. +# If max-open-files = -1, RocksDB will prefetch index and filter blocks into +# block cache at startup, so if your database has a large working set, it will +# take several minutes to open the db. +max-open-files = 1024 + +# Max size of rocksdb's MANIFEST file. +# For detailed explanation please refer to https://github.com/facebook/rocksdb/wiki/MANIFEST +# max-manifest-file-size = "20MB" + +# If true, the database will be created if it is missing. +# create-if-missing = true + +# rocksdb wal recovery mode +# 0 : TolerateCorruptedTailRecords, tolerate incomplete record in trailing data on all logs; +# 1 : AbsoluteConsistency, We don't expect to find any corruption in the WAL; +# 2 : PointInTimeRecovery, Recover to point-in-time consistency; +# 3 : SkipAnyCorruptedRecords, Recovery after a disaster; +# wal-recovery-mode = 2 + +# rocksdb write-ahead logs dir path +# This specifies the absolute dir path for write-ahead logs (WAL). +# If it is empty, the log files will be in the same dir as data. +# When you set the path to rocksdb directory in memory like in /dev/shm, you may want to set +# wal-dir to a directory on a persistent storage. +# See https://github.com/facebook/rocksdb/wiki/How-to-persist-in-memory-RocksDB-database +# wal-dir = "/tmp/tikv/store" + +# The following two fields affect how archived write-ahead logs will be deleted. +# 1. If both set to 0, logs will be deleted asap and will not get into the archive. +# 2. If wal-ttl-seconds is 0 and wal-size-limit is not 0, +# WAL files will be checked every 10 min and if total size is greater +# then wal-size-limit, they will be deleted starting with the +# earliest until size_limit is met. All empty files will be deleted. +# 3. If wal-ttl-seconds is not 0 and wal-size-limit is 0, then +# WAL files will be checked every wal-ttl-seconds / 2 and those that +# are older than wal-ttl-seconds will be deleted. +# 4. If both are not 0, WAL files will be checked every 10 min and both +# checks will be performed with ttl being first. +# When you set the path to rocksdb directory in memory like in /dev/shm, you may want to set +# wal-ttl-seconds to a value greater than 0 (like 86400) and backup your db on a regular basis. +# See https://github.com/facebook/rocksdb/wiki/How-to-persist-in-memory-RocksDB-database +# wal-ttl-seconds = 0 +# wal-size-limit = 0 + +# rocksdb max total wal size +# max-total-wal-size = "4GB" + +# Rocksdb Statistics provides cumulative stats over time. +# Turn statistics on will introduce about 5%-10% overhead for RocksDB, +# but it is worthy to know the internal status of RocksDB. +# enable-statistics = true + +# Dump statistics periodically in information logs. +# Same as rocksdb's default value (10 min). +# stats-dump-period = "10m" + +# Due to Rocksdb FAQ: https://github.com/facebook/rocksdb/wiki/RocksDB-FAQ, +# If you want to use rocksdb on multi disks or spinning disks, you should set value at +# least 2MB; +# compaction-readahead-size = 0 + +# This is the maximum buffer size that is used by WritableFileWrite +# writable-file-max-buffer-size = "1MB" + +# Use O_DIRECT for both reads and writes in background flush and compactions +# use-direct-io-for-flush-and-compaction = false + +# Limit the disk IO of compaction and flush. Compaction and flush can cause +# terrible spikes if they exceed a certain threshold. Consider setting this to +# 50% ~ 80% of the disk throughput for a more stable result. However, in heavy +# write workload, limiting compaction and flush speed can cause write stalls too. +# rate-bytes-per-sec = 0 + +# Enable or disable the pipelined write +# enable-pipelined-write = true + +# Allows OS to incrementally sync files to disk while they are being +# written, asynchronously, in the background. +# bytes-per-sync = "0MB" + +# Allows OS to incrementally sync WAL to disk while it is being written. +# wal-bytes-per-sync = "0KB" + +# Specify the maximal size of the Rocksdb info log file. If the log file +# is larger than `max_log_file_size`, a new info log file will be created. +# If max_log_file_size == 0, all logs will be written to one log file. +# Default: 1GB +# info-log-max-size = "1GB" + +# Time for the Rocksdb info log file to roll (in seconds). +# If specified with non-zero value, log file will be rolled +# if it has been active longer than `log_file_time_to_roll`. +# Default: 0 (disabled) +# info-log-roll-time = "0" + +# Maximal Rocksdb info log files to be kept. +# Default: 10 +# info-log-keep-log-file-num = 10 + +# This specifies the Rocksdb info LOG dir. +# If it is empty, the log files will be in the same dir as data. +# If it is non empty, the log files will be in the specified dir, +# and the db data dir's absolute path will be used as the log file +# name's prefix. +# Default: empty +# info-log-dir = "" + +# Column Family default used to store actual data of the database. +[rocksdb.defaultcf] +# compression method (if any) is used to compress a block. +# no: kNoCompression +# snappy: kSnappyCompression +# zlib: kZlibCompression +# bzip2: kBZip2Compression +# lz4: kLZ4Compression +# lz4hc: kLZ4HCCompression +# zstd: kZSTD + +# per level compression +# compression-per-level = ["no", "no", "lz4", "lz4", "lz4", "zstd", "zstd"] + +# Approximate size of user data packed per block. Note that the +# block size specified here corresponds to uncompressed data. +# block-size = "64KB" + +# If you're doing point lookups you definitely want to turn bloom filters on, We use +# bloom filters to avoid unnecessary disk reads. Default bits_per_key is 10, which +# yields ~1% false positive rate. Larger bits_per_key values will reduce false positive +# rate, but increase memory usage and space amplification. +# bloom-filter-bits-per-key = 10 + +# false means one sst file one bloom filter, true means evry block has a corresponding bloom filter +# block-based-bloom-filter = false + +# level0-file-num-compaction-trigger = 4 + +# Soft limit on number of level-0 files. We start slowing down writes at this point. +# level0-slowdown-writes-trigger = 20 + +# Maximum number of level-0 files. We stop writes at this point. +# level0-stop-writes-trigger = 36 + +# Amount of data to build up in memory (backed by an unsorted log +# on disk) before converting to a sorted on-disk file. +# write-buffer-size = "128MB" + +# The maximum number of write buffers that are built up in memory. +# max-write-buffer-number = 5 + +# The minimum number of write buffers that will be merged together +# before writing to storage. +# min-write-buffer-number-to-merge = 1 + +# Control maximum total data size for base level (level 1). +# max-bytes-for-level-base = "512MB" + +# Target file size for compaction. +# target-file-size-base = "8MB" + +# Max bytes for compaction.max_compaction_bytes +# max-compaction-bytes = "2GB" + +# There are four different algorithms to pick files to compact. +# 0 : ByCompensatedSize +# 1 : OldestLargestSeqFirst +# 2 : OldestSmallestSeqFirst +# 3 : MinOverlappingRatio +# compaction-pri = 3 + +# block-cache used to cache uncompressed blocks, big block-cache can speed up read. +# in normal cases should tune to 30%-50% system's total memory. +# block-cache-size = "1GB" + +# Indicating if we'd put index/filter blocks to the block cache. +# If not specified, each "table reader" object will pre-load index/filter block +# during table initialization. +# cache-index-and-filter-blocks = true + +# Pin level0 filter and index blocks in cache. +# pin-l0-filter-and-index-blocks = true + +# Enable read amplication statistics. +# value => memory usage (percentage of loaded blocks memory) +# 1 => 12.50 % +# 2 => 06.25 % +# 4 => 03.12 % +# 8 => 01.56 % +# 16 => 00.78 % +# read-amp-bytes-per-bit = 0 + +# Pick target size of each level dynamically. +# dynamic-level-bytes = true + +# Options for Column Family write +# Column Family write used to store commit informations in MVCC model +[rocksdb.writecf] +# compression-per-level = ["no", "no", "lz4", "lz4", "lz4", "zstd", "zstd"] +# block-size = "64KB" +# write-buffer-size = "128MB" +# max-write-buffer-number = 5 +# min-write-buffer-number-to-merge = 1 +# max-bytes-for-level-base = "512MB" +# target-file-size-base = "8MB" + +# in normal cases should tune to 10%-30% system's total memory. +# block-cache-size = "256MB" +# level0-file-num-compaction-trigger = 4 +# level0-slowdown-writes-trigger = 20 +# level0-stop-writes-trigger = 36 +# cache-index-and-filter-blocks = true +# pin-l0-filter-and-index-blocks = true +# compaction-pri = 3 +# read-amp-bytes-per-bit = 0 +# dynamic-level-bytes = true + +[rocksdb.lockcf] +# compression-per-level = ["no", "no", "no", "no", "no", "no", "no"] +# block-size = "16KB" +# write-buffer-size = "128MB" +# max-write-buffer-number = 5 +# min-write-buffer-number-to-merge = 1 +# max-bytes-for-level-base = "128MB" +# target-file-size-base = "8MB" +# block-cache-size = "256MB" +# level0-file-num-compaction-trigger = 1 +# level0-slowdown-writes-trigger = 20 +# level0-stop-writes-trigger = 36 +# cache-index-and-filter-blocks = true +# pin-l0-filter-and-index-blocks = true +# compaction-pri = 0 +# read-amp-bytes-per-bit = 0 +# dynamic-level-bytes = true + +[raftdb] +# max-sub-compactions = 1 +max-open-files = 1024 +# max-manifest-file-size = "20MB" +# create-if-missing = true + +# enable-statistics = true +# stats-dump-period = "10m" + +# compaction-readahead-size = 0 +# writable-file-max-buffer-size = "1MB" +# use-direct-io-for-flush-and-compaction = false +# enable-pipelined-write = true +# allow-concurrent-memtable-write = false +# bytes-per-sync = "0MB" +# wal-bytes-per-sync = "0KB" + +# info-log-max-size = "1GB" +# info-log-roll-time = "0" +# info-log-keep-log-file-num = 10 +# info-log-dir = "" + +[raftdb.defaultcf] +# compression-per-level = ["no", "no", "lz4", "lz4", "lz4", "zstd", "zstd"] +# block-size = "64KB" +# write-buffer-size = "128MB" +# max-write-buffer-number = 5 +# min-write-buffer-number-to-merge = 1 +# max-bytes-for-level-base = "512MB" +# target-file-size-base = "8MB" + +# should tune to 256MB~2GB. +# block-cache-size = "256MB" +# level0-file-num-compaction-trigger = 4 +# level0-slowdown-writes-trigger = 20 +# level0-stop-writes-trigger = 36 +# cache-index-and-filter-blocks = true +# pin-l0-filter-and-index-blocks = true +# compaction-pri = 0 +# read-amp-bytes-per-bit = 0 +# dynamic-level-bytes = true + +[security] +# set the path for certificates. Empty string means disabling secure connectoins. +# ca-path = "" +# cert-path = "" +# key-path = "" + +[import] +# the directory to store importing kv data. +# import-dir = "/tmp/tikv/import" +# number of threads to handle RPC requests. +# num-threads = 8 +# stream channel window size, stream will be blocked on channel full. +# stream-channel-window = 128 diff --git a/docker/tidb/dashboard-installer/Dockerfile b/docker/tidb/dashboard-installer/Dockerfile new file mode 100755 index 0000000..16878b8 --- /dev/null +++ b/docker/tidb/dashboard-installer/Dockerfile @@ -0,0 +1,7 @@ +FROM python:2.7-alpine + +RUN apk add --no-cache ca-certificates curl + +ADD dashboards / + +ENTRYPOINT ["/tidb-dashboard-installer.sh"] diff --git a/docker/tidb/dashboard-installer/README.md b/docker/tidb/dashboard-installer/README.md new file mode 100755 index 0000000..4fe583d --- /dev/null +++ b/docker/tidb/dashboard-installer/README.md @@ -0,0 +1,13 @@ +# TiDB dashboard installer + +This image is used to configure Grafana datasource and dashboards for TiDB cluster. It is used in [tidb-docker-compose](https://github.com/pingcap/tidb-docker-compose) and [tidb-operator](https://github.com/pingcap/tidb-operator). + +The JSON files in dashboards are copied from [tidb-ansible](https://github.com/pingcap/tidb-ansible/tree/master/scripts). + +Grafana version prior to v5.0.0 can only use import API to automate datasource and dashboard configuration. So this image is needed to run in docker environment. It runs only once in this environment. + +With Grafana v5.x, we can use [provisioning](http://docs.grafana.org/administration/provisioning) feature to statically provision datasources and dashboards. No need to use scripts to configure Grafana. + +But currently, the dashboards in [tidb-ansible](https://github.com/pingcap/tidb-ansible/tree/master/scripts) repository are incompatible with Grafana v5.x and cannot be statically provisioned. So this image is still required. + +In the future, we can use [grafonnet](https://github.com/grafana/grafonnet-lib) to migrate old dashboards and make dashboard updating reviewable. diff --git a/docker/tidb/dashboard-installer/dashboards/LICENSE b/docker/tidb/dashboard-installer/dashboards/LICENSE new file mode 100755 index 0000000..b67d909 --- /dev/null +++ b/docker/tidb/dashboard-installer/dashboards/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/docker/tidb/dashboard-installer/dashboards/datasource.json b/docker/tidb/dashboard-installer/dashboards/datasource.json new file mode 100755 index 0000000..dbf959c --- /dev/null +++ b/docker/tidb/dashboard-installer/dashboards/datasource.json @@ -0,0 +1,7 @@ +{ + "name": "tidb-cluster", + "type": "prometheus", + "url": "http://127.0.0.1:9090", + "access": "proxy", + "basicAuth": false +} diff --git a/docker/tidb/dashboard-installer/dashboards/dests.json b/docker/tidb/dashboard-installer/dashboards/dests.json new file mode 100755 index 0000000..85cd1c6 --- /dev/null +++ b/docker/tidb/dashboard-installer/dashboards/dests.json @@ -0,0 +1,12 @@ +[ + { + "datasource": "tidb-cluster", + "name": "TiDB-Cluster", + "titles": { + "overview": "TiDB-Cluster-Overview", + "pd": "TiDB-Cluster-PD", + "tidb": "TiDB-Cluster-TiDB", + "tikv": "TiDB-Cluster-TiKV" + } + } +] diff --git a/docker/tidb/dashboard-installer/dashboards/grafana-config-copy.py b/docker/tidb/dashboard-installer/dashboards/grafana-config-copy.py new file mode 100755 index 0000000..e2c1abf --- /dev/null +++ b/docker/tidb/dashboard-installer/dashboards/grafana-config-copy.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python + +from __future__ import print_function, \ + unicode_literals + +import urllib +import urllib2 +import base64 +import json +import sys +from pprint import pprint + +try: + input = raw_input +except: + pass + +############################################################ +################## CONFIGURATION ########################### +############################################################ + +# use a viewer key +src = dict( + dashboards={ + "pd" : 'pd.json', + "tidb": 'tidb.json', + "tikv": 'tikv.json', + "overview": 'overview.json' + }) + +dests = [ +] + +if not dests: + with open("./dests.json") as fp: + dests = json.load(fp) + + +############################################################ +################## CONFIGURATION ENDS ###################### +############################################################ + +def export_dashboard(api_url, api_key, dashboard_name): + req = urllib2.Request(api_url + 'api/dashboards/db/' + dashboard_name, + headers={'Authorization': "Bearer {}".format(api_key)}) + + resp = urllib2.urlopen(req) + data = json.load(resp) + return data['dashboard'] + + +def fill_dashboard_with_dest_config(dashboard, dest, type_='node'): + dashboard['title'] = dest['titles'][type_] + dashboard['id'] = None +# pprint(dashboard) + for row in dashboard['rows']: + for panel in row['panels']: + panel['datasource'] = dest['datasource'] + + if 'templating' in dashboard: + for templating in dashboard['templating']['list']: + if templating['type'] == 'query': + templating['current'] = {} + templating['options'] = [] + templating['datasource'] = dest['datasource'] + + if 'annotations' in dashboard: + for annotation in dashboard['annotations']['list']: + annotation['datasource'] = dest['datasource'] + return dashboard + +def import_dashboard_via_user_pass(api_url, user, password, dashboard): + payload = {'dashboard': dashboard, + 'overwrite': True} + auth_string = base64.b64encode('%s:%s' % (user, password)) + headers = {'Authorization': "Basic {}".format(auth_string), + 'Content-Type': 'application/json'} + req = urllib2.Request(api_url + 'api/dashboards/db', + headers=headers, + data=json.dumps(payload)) + try: + resp = urllib2.urlopen(req) + data = json.load(resp) + return data + except urllib2.HTTPError, error: + data = json.load(error) + return data + + +if __name__ == '__main__': + url = sys.argv[1] + user = sys.argv[2] + password = sys.argv[3] + print(url) + for type_ in src['dashboards']: + print("[load] from <{}>:{}".format( + src['dashboards'][type_], type_)) + + dashboard = json.load(open(src['dashboards'][type_])) + + for dest in dests: + dashboard = fill_dashboard_with_dest_config(dashboard, dest, type_) + print("[import] as <{}> to [{}]".format( + dashboard['title'], dest['name']), end='\t............. ') + ret = import_dashboard_via_user_pass(url, user, password, dashboard) + print(ret) + + if ret['status'] != 'success': + print(' > ERROR: ', ret) + raise RuntimeError diff --git a/docker/tidb/dashboard-installer/dashboards/overview.json b/docker/tidb/dashboard-installer/dashboards/overview.json new file mode 100755 index 0000000..6d54dfd --- /dev/null +++ b/docker/tidb/dashboard-installer/dashboards/overview.json @@ -0,0 +1,4892 @@ +{ + "__inputs": [ + { + "name": "DS_TEST-CLUSTER", + "label": "test-cluster", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "4.6.3" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "singlestat", + "name": "Singlestat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "${DS_TEST-CLUSTER}", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "hideControls": false, + "id": null, + "links": [ + { + "icon": "doc", + "includeVars": true, + "keepTime": true, + "tags": [], + "targetBlank": true, + "title": "Report", + "tooltip": "Open a pdf report for the current dashboard", + "type": "link", + "url": "http://172.16.10.71:8686/api/report/test-cluster-overview?apitoken=eyJrIjoiWTlFZ0M0REpKbFY3aFRvOVNJZEh6cWNJaTkxY3NLdTYiLCJuIjoiZ3JhZmFuYV9hcGlrZXkiLCJpZCI6MX0=" + } + ], + "refresh": "1m", + "rows": [ + { + "collapse": false, + "height": 310, + "panels": [ + { + "columns": [ + { + "text": "Current", + "value": "current" + } + ], + "datasource": "${DS_TEST-CLUSTER}", + "fontSize": "100%", + "hideTimeOverride": true, + "id": 76, + "links": [], + "pageSize": null, + "scroll": true, + "showHeader": true, + "sort": { + "col": null, + "desc": false + }, + "span": 6, + "styles": [ + { + "alias": "Time", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "alias": "Service", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "pattern": "Metric", + "thresholds": [], + "type": "string", + "unit": "short" + }, + { + "alias": "Up", + "colorMode": "cell", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 0, + "pattern": "Current", + "thresholds": [ + "0", + "1" + ], + "type": "number", + "unit": "short" + }, + { + "alias": "", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "expr": "\ncount(probe_success{group=\"tidb\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "TiDB", + "refId": "A" + }, + { + "expr": "\ncount(probe_success{group=\"pd\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "PD", + "refId": "B" + }, + { + "expr": "\ncount(probe_success{group=\"tikv\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "TiKV", + "refId": "C" + }, + { + "expr": "\ncount(probe_success{group=\"pump\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Pump", + "refId": "D" + }, + { + "expr": "\ncount(probe_success{group=\"drainer\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Drainer", + "refId": "E" + }, + { + "expr": "\ncount(probe_success{group=\"kafka\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Kafka", + "refId": "F" + }, + { + "expr": "\ncount(probe_success{group=\"zookeeper\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Zookeeper", + "refId": "G" + }, + { + "expr": "\ncount(probe_success{group=\"node_exporter\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Node_exporter", + "refId": "H" + }, + { + "expr": "\ncount(probe_success{group=\"blackbox_exporter\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Blackbox_exporter", + "refId": "I" + }, + { + "expr": "\ncount(probe_success{group=\"grafana\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Grafana", + "refId": "J" + }, + { + "expr": "\ncount(probe_success{job=\"blackbox_exporter_http\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Pushgateway", + "refId": "K" + }, + { + "expr": "\ncount(probe_success{group=\"kafka_exporter\"} == 1)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Kafka_exporter", + "refId": "L" + } + ], + "timeFrom": "1s", + "title": "", + "transform": "timeseries_aggregations", + "type": "table" + }, + { + "columns": [ + { + "text": "Current", + "value": "current" + } + ], + "datasource": "${DS_TEST-CLUSTER}", + "fontSize": "100%", + "hideTimeOverride": true, + "id": 77, + "links": [], + "pageSize": null, + "scroll": true, + "showHeader": true, + "sort": { + "col": null, + "desc": false + }, + "span": 6, + "styles": [ + { + "alias": "Time", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "alias": "Service", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "pattern": "Metric", + "thresholds": [], + "type": "string", + "unit": "short" + }, + { + "alias": "Down", + "colorMode": "cell", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 0, + "pattern": "Current", + "thresholds": [ + "100", + "200" + ], + "type": "number", + "unit": "short" + }, + { + "alias": "", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "expr": "\ncount(probe_success{group=\"tidb\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "TiDB", + "refId": "A" + }, + { + "expr": "\ncount(probe_success{group=\"pd\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "PD", + "refId": "B" + }, + { + "expr": "\ncount(probe_success{group=\"tikv\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "TiKV", + "refId": "C" + }, + { + "expr": "\ncount(probe_success{group=\"pump\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Pump", + "refId": "D" + }, + { + "expr": "\ncount(probe_success{group=\"drainer\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Drainer", + "refId": "E" + }, + { + "expr": "\ncount(probe_success{group=\"kafka\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Kafka", + "refId": "F" + }, + { + "expr": "\ncount(probe_success{group=\"zookeeper\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Zookeeper", + "refId": "G" + }, + { + "expr": "\ncount(probe_success{group=\"node_exporter\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Node_exporter", + "refId": "H" + }, + { + "expr": "\ncount(probe_success{group=\"blackbox_exporter\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Blackbox_exporter", + "refId": "I" + }, + { + "expr": "\ncount(probe_success{group=\"grafana\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Grafana", + "refId": "J" + }, + { + "expr": "\ncount(probe_success{job=\"blackbox_exporter_http\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Pushgateway", + "refId": "K" + }, + { + "expr": "\ncount(probe_success{group=\"kafka_exporter\"} == 0)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Kafka_exporter", + "refId": "L" + } + ], + "timeFrom": "1s", + "title": "", + "transform": "timeseries_aggregations", + "type": "table" + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Services Port Status", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 29, + "interval": null, + "links": [], + "mappingType": 2, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "1", + "text": "Leader", + "to": "100000" + }, + { + "from": "0", + "text": "Follower", + "to": "1" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "delta(pd_server_tso{type=\"save\",instance=\"$instance\"}[1m])", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "", + "metric": "pd_server_tso", + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "PD Role", + "type": "singlestat", + "valueFontSize": "50%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "1" + }, + { + "op": "=", + "text": "", + "value": "" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "format": "bytes", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": false + }, + "id": 27, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(77, 135, 25, 0.18)", + "full": true, + "lineColor": "rgb(21, 179, 65)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"storage_capacity\"}", + "intervalFactor": 2, + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "Storage Capacity", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "format": "bytes", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "hideTimeOverride": false, + "id": 28, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"storage_size\"}", + "intervalFactor": 2, + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "Current Storage Size", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 30, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"region_count\"}", + "intervalFactor": 2, + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "Number of Regions", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "${DS_TEST-CLUSTER}", + "format": "percentunit", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 64, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 1, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "1 - min(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_score\"}) / max(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_score\"})", + "format": "time_series", + "interval": "15s", + "intervalFactor": 2, + "refId": "A" + } + ], + "thresholds": "0.01,0.5", + "title": "Leader Balance Ratio", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "${DS_TEST-CLUSTER}", + "format": "percentunit", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 65, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 1, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "1 - min(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_score\"}) / max(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_score\"})", + "format": "time_series", + "interval": "15s", + "intervalFactor": 2, + "refId": "A" + } + ], + "thresholds": "0.05,0.5", + "title": "Region Balance Ratio", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "columns": [ + { + "text": "Current", + "value": "current" + } + ], + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fontSize": "100%", + "hideTimeOverride": false, + "id": 18, + "links": [], + "pageSize": null, + "repeat": null, + "scroll": false, + "showHeader": true, + "sort": { + "col": null, + "desc": false + }, + "span": 2, + "styles": [ + { + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Metric", + "sanitize": false, + "type": "string" + }, + { + "colorMode": "cell", + "colors": [ + "rgba(50, 172, 45, 0.97)", + "rgba(237, 129, 40, 0.89)", + "rgba(245, 54, 54, 0.9)" + ], + "decimals": 0, + "pattern": "Current", + "thresholds": [ + "1", + "2" + ], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_up_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Up Stores", + "metric": "pd_cluster_status", + "refId": "A", + "step": 30 + }, + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_disconnected_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Disconnect Stores", + "metric": "pd_cluster_status", + "refId": "B", + "step": 30 + }, + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_low_space_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "LowSpace Stores", + "metric": "pd_cluster_status", + "refId": "C", + "step": 30 + }, + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_down_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Down Stores", + "metric": "pd_cluster_status", + "refId": "D", + "step": 30 + }, + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_offline_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Offline Stores", + "metric": "pd_cluster_status", + "refId": "E", + "step": 30 + }, + { + "expr": "pd_cluster_status{instance=\"$instance\",type=\"store_tombstone_count\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Tombstone Stores", + "metric": "pd_cluster_status", + "refId": "F", + "step": 30 + } + ], + "title": "Store Status", + "transform": "timeseries_aggregations", + "transparent": false, + "type": "table" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 24, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(grpc_server_handling_seconds_bucket{instance=\"$instance\"}[5m])) by (grpc_method, le))", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{grpc_method}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% completed_cmds_duration_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 32, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.98, sum(rate(pd_client_request_handle_requests_duration_seconds_bucket[30s])) by (type, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}} 98th percentile", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(rate(pd_client_request_handle_requests_duration_seconds_sum[30s])) by (type) / avg(rate(pd_client_request_handle_requests_duration_seconds_count[30s])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}} average", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "handle_requests_duration_seconds", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "s", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 66, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_regions_status{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + }, + { + "expr": "sum(pd_regions_status) by (instance, type)", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region Health", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 68, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_write_region_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot write region's leader distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 0, + "fill": 0, + "id": 69, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_read_region_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot read region's leader distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 33, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_region_heartbeat{instance=\"$instance\", type=\"report\", status=\"ok\"}[1m])) by (store)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region heartbeat report", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 67, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(pd_scheduler_region_heartbeat_latency_seconds_bucket[5m])) by (store, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% region heartbeat latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": "instance", + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "PD", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 2, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_executor_statement_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Statement OPS", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 34, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "hideEmpty": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 3, + "legendFormat": "99", + "refId": "B", + "step": 15 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "95", + "refId": "C" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 35, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(tidb_server_query_total[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} {{type}} {{result}}", + "refId": "A", + "step": 20 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "QPS By Instance", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 72, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_server_execute_error_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": " {{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Failed Query OPM", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 4, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "fill": 0, + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": true, + "targets": [ + { + "expr": "tidb_server_connections", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(tidb_server_connections)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "total", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Connection Count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 36, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "go_memstats_heap_inuse_bytes{job=~\"tidb.*\"}", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{job}}", + "metric": "go_memstats_heap_inuse_bytes", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Heap Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 70, + "legend": { + "avg": false, + "current": true, + "max": true, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_session_transaction_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Transaction OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 71, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "95", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Transaction Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 37, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_txn_cmd_duration_seconds_count[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Cmd OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 38, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_tikvclient_txn_cmd_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Cmd Duration 99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 39, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_client_cmd_handle_cmds_duration_seconds_count{type=\"tso\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "cmd", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(pd_client_request_handle_requests_duration_seconds_count{type=\"tso\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "request", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD TSO OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 40, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "hideEmpty": false, + "hideZero": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD TSO Wait Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "fill": 0, + "id": 41, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_region_err_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tidb_server_session_execute_parse_duration_count", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "TiClient Region Error OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 42, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_lock_resolver_actions_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tidb_tikvclient_lock_resolver_actions_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Lock Resolve OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 6, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": false, + "hideZero": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_domain_load_schema_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Load Schema Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 43, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": true, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_backoff_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Backoff OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "TiDB", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 299, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 20, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{type=\"leader\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 10 + }, + { + "expr": "delta(tikv_pd_heartbeat_tick_total{type=\"leader\"}[30s]) < -10", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "total", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "leader", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 21, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{type=\"region\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "region", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 75, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total[1m])) by (instance,job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{job}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "CPU", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 74, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(process_resident_memory_bytes{job=~\"tikv.*\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memory", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 5, + "id": 44, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 0, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_size_bytes) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "store size", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 73, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_size_bytes) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "cf size", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 3, + "grid": {}, + "id": 17, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_channel_full_total[1m])) by (job, type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}} - {{type}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "channel full", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 11, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_server_report_failure_msg_total[1m])) by (type,instance,job,store_id)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}} - {{type}} - to - {{store_id}}", + "metric": "tikv_server_raft_store_msg_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "server report failures", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 46, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_scheduler_contex_total) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler pending commands", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 45, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": false, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_pending_request[1m])) by (req, priority)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ req }} - {{priority}}", + "metric": "tikv_region_written_keys_bucket", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_coprocessor_pending_request[1m])) BY (type, instance)", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor pending requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 51, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_executor_count[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_coprocessor_request_error", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor executor count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 5, + "fill": 1, + "id": 47, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_coprocessor_request_duration_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-99%", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_duration_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-95%", + "refId": "B", + "step": 10 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_duration_seconds_sum{req=\"select\"}[1m])) / sum(rate(tikv_coprocessor_request_duration_seconds_count{req=\"select\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "select-avg", + "refId": "C", + "step": 10 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_duration_seconds_sum{req=\"index\"}[1m])) / sum(rate(tikv_coprocessor_request_duration_seconds_count{req=\"index\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "index-avg", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor request duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 48, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"raftstore_.*\"}[1m])) by (job, name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "raft store CPU", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 49, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"cop_.*\"}[1m])) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_coprocessor_request_duration_seconds_bucket", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Coprocessor CPU", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "TiKV", + "titleSize": "h6" + }, + { + "collapse": false, + "height": 250, + "panels": [ + { + "columns": [ + { + "text": "Current", + "value": "current" + } + ], + "datasource": "${DS_TEST-CLUSTER}", + "fontSize": "100%", + "hideTimeOverride": false, + "id": 57, + "links": [], + "pageSize": null, + "scroll": true, + "showHeader": true, + "sort": { + "col": null, + "desc": false + }, + "span": 3, + "styles": [ + { + "alias": "Host", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "link": false, + "pattern": "Metric", + "preserveFormat": false, + "sanitize": false, + "thresholds": [], + "type": "string", + "unit": "short" + }, + { + "alias": "CPU Num", + "colorMode": "value", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(30, 232, 22, 0.97)" + ], + "decimals": 0, + "link": false, + "pattern": "Current", + "thresholds": [ + "0", + "1" + ], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "expr": "count(node_cpu{mode=\"user\"}) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ instance }}", + "refId": "A", + "step": 2 + } + ], + "timeFrom": null, + "title": "Vcores", + "transform": "timeseries_aggregations", + "type": "table" + }, + { + "columns": [ + { + "text": "Current", + "value": "current" + } + ], + "datasource": "${DS_TEST-CLUSTER}", + "fontSize": "100%", + "hideTimeOverride": true, + "id": 59, + "links": [], + "pageSize": null, + "scroll": true, + "showHeader": true, + "sort": { + "col": null, + "desc": false + }, + "span": 3, + "styles": [ + { + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "sanitize": false, + "type": "hidden" + }, + { + "alias": "Host", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "pattern": "Metric", + "thresholds": [], + "type": "string", + "unit": "short" + }, + { + "alias": "Memory", + "colorMode": "value", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 0, + "pattern": "Current", + "thresholds": [ + "0", + "1" + ], + "type": "number", + "unit": "bytes" + } + ], + "targets": [ + { + "expr": "node_memory_MemTotal", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ instance }}", + "refId": "A", + "step": 2 + } + ], + "timeFrom": "1s", + "title": "Memory", + "transform": "timeseries_aggregations", + "type": "table" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 55, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "100 - avg by (instance) (irate(node_cpu{mode=\"idle\"}[1m]) ) * 100", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 78, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "node_load1", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Load [1m]", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 58, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "node_memory_MemAvailable", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ instance }}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memory Available", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 79, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "irate(node_network_receive_bytes{device!=\"lo\"}[5m]) * 8", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Inbound: {{instance}}", + "refId": "A" + }, + { + "expr": "irate(node_network_transmit_bytes{device!=\"lo\"}[5m]) * 8", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Outbound: {{instance}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Network Traffic", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 60, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "irate(node_netstat_TcpExt_TCPSynRetrans[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - TCPSynRetrans", + "refId": "A", + "step": 10 + }, + { + "expr": "irate(node_netstat_TcpExt_TCPSlowStartRetrans[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - TCPSlowStartRetrans", + "refId": "B", + "step": 10 + }, + { + "expr": "irate(node_netstat_TcpExt_TCPForwardRetrans[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - TCPForwardRetrans", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "TCP Retrans", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 0, + "fill": 1, + "id": 61, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(node_disk_io_time_ms[1m]) / 1000", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - {{device}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "IO Util", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "System Info", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "${DS_TEST-CLUSTER}", + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "instance", + "options": [], + "query": "label_values(pd_cluster_status, instance)", + "refresh": 1, + "regex": "", + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": {}, + "datasource": "${DS_TEST-CLUSTER}", + "hide": 0, + "includeAll": true, + "label": "Namespace", + "multi": false, + "name": "namespace", + "options": [], + "query": "label_values(pd_cluster_status{instance=\"$instance\"}, namespace)", + "refresh": 1, + "regex": "", + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "Test-Cluster-Overview", + "version": 6 +} \ No newline at end of file diff --git a/docker/tidb/dashboard-installer/dashboards/pd.json b/docker/tidb/dashboard-installer/dashboards/pd.json new file mode 100755 index 0000000..c03f189 --- /dev/null +++ b/docker/tidb/dashboard-installer/dashboards/pd.json @@ -0,0 +1,5698 @@ +{ + "__inputs": [ + { + "name": "DS_TEST-CLUSTER", + "label": "test-cluster", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "4.6.3" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "singlestat", + "name": "Singlestat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "${DS_TEST-CLUSTER}", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 1, + "hideControls": false, + "id": null, + "links": [ + { + "icon": "doc", + "tags": [], + "title": "Report", + "tooltip": "Open a pdf report for the current dashboard", + "type": "link", + "url": "http://172.16.10.49:8686/api/report/test-cluster-pd?apitoken=eyJrIjoiQkU2bmFQdjZMeTRmWVJ3UFo0bmV3Ym5zRG04Y2hmMHYiLCJuIjoiZ3JhZmFuYV9hcGlrZXkiLCJpZCI6MX0=" + } + ], + "refresh": "30s", + "rows": [ + { + "collapse": false, + "height": 299, + "panels": [ + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 55, + "interval": null, + "links": [], + "mappingType": 2, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "1", + "text": "Leader", + "to": "100000" + }, + { + "from": "0", + "text": "Follower", + "to": "1" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "delta(pd_server_tso{type=\"save\",instance=\"$instance\"}[1m])", + "intervalFactor": 2, + "legendFormat": "", + "metric": "pd_server_tso", + "refId": "A", + "step": 40 + } + ], + "thresholds": "", + "title": "PD Role", + "type": "singlestat", + "valueFontSize": "50%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "format": "decbytes", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": false + }, + "id": 10, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(77, 135, 25, 0.18)", + "full": true, + "lineColor": "rgb(21, 179, 65)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(pd_cluster_status{instance=\"$instance\",namespace=~\"$namespace\",type=\"storage_capacity\"})", + "intervalFactor": 2, + "refId": "A", + "step": 40 + } + ], + "thresholds": "", + "title": "Storage Capacity", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "format": "decbytes", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "hideTimeOverride": false, + "id": 38, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(pd_cluster_status{instance=\"$instance\",namespace=~\"$namespace\",type=\"storage_size\"})", + "intervalFactor": 2, + "refId": "A", + "step": 40 + } + ], + "thresholds": "", + "title": "Current Storage Size", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": false + }, + "id": 20, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 2, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(pd_cluster_status{instance=\"$instance\",namespace=~\"$namespace\",type=\"leader_count\"})", + "intervalFactor": 2, + "refId": "A", + "step": 40 + } + ], + "thresholds": "", + "title": "Number of Regions", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": [ + "rgba(50, 172, 45, 0.97)", + "rgba(237, 129, 40, 0.89)", + "rgba(245, 54, 54, 0.9)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "format": "percentunit", + "gauge": { + "maxValue": 1, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "hideTimeOverride": false, + "id": 37, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 1, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "1 - min(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_score\"}) / max(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_score\"})", + "intervalFactor": 2, + "refId": "A", + "step": 40 + } + ], + "thresholds": "0.01,0.5", + "title": "Leader Balance Ratio", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": [ + "rgba(50, 172, 45, 0.97)", + "rgba(237, 129, 40, 0.89)", + "rgba(245, 54, 54, 0.9)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "format": "percentunit", + "gauge": { + "maxValue": 1, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 36, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 1, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "1 - min(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_score\"}) / max(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_score\"})", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A", + "step": 40 + } + ], + "thresholds": "0.05,0.5", + "title": "Region Balance Ratio", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "columns": [ + { + "text": "Current", + "value": "current" + } + ], + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fontSize": "100%", + "hideTimeOverride": false, + "id": 39, + "links": [], + "pageSize": null, + "repeat": null, + "scroll": false, + "showHeader": true, + "sort": { + "col": null, + "desc": false + }, + "span": 2, + "styles": [ + { + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Metric", + "sanitize": false, + "type": "string" + }, + { + "colorMode": "cell", + "colors": [ + "rgba(50, 172, 45, 0.97)", + "rgba(237, 129, 40, 0.89)", + "rgba(245, 54, 54, 0.9)" + ], + "decimals": 0, + "pattern": "Current", + "thresholds": [ + "1", + "2" + ], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_up_count\"})", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Up Stores", + "metric": "pd_cluster_status", + "refId": "A", + "step": 20 + }, + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_disconnected_count\"})", + "intervalFactor": 2, + "legendFormat": "Disconnect Stores", + "refId": "B", + "step": 20 + }, + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_unhealth_count\"})", + "intervalFactor": 2, + "legendFormat": "Unhealth Stores", + "refId": "C", + "step": 20 + }, + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_low_space_count\"})", + "intervalFactor": 2, + "legendFormat": "LowSpace Stores", + "refId": "D", + "step": 20 + }, + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_down_count\"})", + "intervalFactor": 2, + "legendFormat": "Down Stores", + "refId": "E", + "step": 20 + }, + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_offline_count\"})", + "intervalFactor": 2, + "legendFormat": "Offline Stores", + "refId": "F", + "step": 20 + }, + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\",type=\"store_tombstone_count\"})", + "intervalFactor": 2, + "legendFormat": "Tombstone Stores", + "refId": "G", + "step": 20 + } + ], + "title": "Store Status", + "transform": "timeseries_aggregations", + "transparent": false, + "type": "table" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 9, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "used ratio", + "yaxis": 2 + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"storage_size\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "strage size", + "refId": "A", + "step": 4 + }, + { + "expr": "avg(pd_cluster_status{type=\"storage_size\", namespace=~\"$namespace\"}) / avg(pd_cluster_status{type=\"storage_capacity\", namespace=~\"$namespace\"})", + "format": "time_series", + "hide": false, + "intervalFactor": 4, + "legendFormat": "used ratio", + "refId": "B", + "step": 8 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Current Storage Usage", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 18, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(pd_cluster_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_count\"})", + "intervalFactor": 2, + "legendFormat": "count", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Current Peer Count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 76, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_cluster_metadata{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Metadata Information", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 75, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_regions_label_level{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region Label Isolation Level", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 100 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "60s", + "handler": 1, + "message": "Regions are unhealthy", + "name": "region health alert", + "noDataState": "keep_state", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 72, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_regions_status{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + }, + { + "expr": "sum(pd_regions_status) by (instance, type)", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 100 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Region Health", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Cluster", + "titleSize": "h4" + }, + { + "collapse": true, + "height": 214, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 83, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_capacity\"}", + "format": "time_series", + "hide": false, + "instant": false, + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store capacity", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 91, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_available\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store available", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 90, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_used\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store used", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 85, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_size\"}) by (store) / sum(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_used\"}) by (store) * 2^20", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Size amplification", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 84, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_available\"}) by (store) / sum(pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"store_capacity\"}) by (store)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store available ratio", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 40, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_score\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 1000000000 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Store leader score", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 41, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": false, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_score\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 1000000000 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Store region score", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 56, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_size\"}", + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store leader size", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "mbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 57, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_size\"}", + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store region size", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "mbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 58, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"leader_count\"}", + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store leader count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 59, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_store_status{instance=\"$instance\", namespace=~\"$namespace\", type=\"region_count\"}", + "intervalFactor": 2, + "legendFormat": "tikv-{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Store region count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Balance", + "titleSize": "h4" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 0, + "fill": 0, + "id": 50, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_write_region_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot write region's leader distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 0, + "fill": 0, + "id": 51, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_write_region_as_peer\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot write region's peer distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "fill": 1, + "id": 48, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"total_written_bytes_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "metric": "pd_hotspot_status", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot write region's leader written bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "fill": 1, + "id": 49, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"total_written_bytes_as_peer\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot region's peer written bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 0, + "fill": 0, + "id": 60, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_read_region_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot read region's leader distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 0, + "fill": 0, + "id": 61, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"hot_read_region_as_peer\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot read region's peer distribution", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 62, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"total_read_bytes_as_leader\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "metric": "pd_hotspot_status", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot read region's leader read bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 63, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "pd_hotspot_status{instance=\"$instance\",type=\"total_read_bytes_as_peer\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Hot read region's peer read bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "HotRegion", + "titleSize": "h4" + }, + { + "collapse": true, + "height": 288, + "panels": [ + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 46, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 1, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "pd_scheduler_status{type=\"allow\",instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{kind}}", + "metric": "pd_scheduler_status", + "refId": "A", + "step": 2 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler is running", + "tooltip": { + "shared": true, + "sort": 1, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 87, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "total", + "sortDesc": true, + "total": true, + "values": true + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_balance_leader{instance=\"$instance\", type=\"move_leader\"}[30s])) by (store)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance leader movement", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "hideTimeOverride": false, + "id": 86, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "total", + "sortDesc": true, + "total": true, + "values": true + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_balance_region{instance=\"$instance\", type=\"move_peer\"}[1m])) by (store)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance region movement", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 89, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": true, + "values": true + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_balance_leader{instance=\"$instance\", type!=\"move_leader\"}[30s])) by (type, store)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}_{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance leader event", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 88, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sortDesc": false, + "total": true, + "values": true + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_balance_region{instance=\"$instance\", type!=\"move_peer\"}[30s])) by (type, store)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}_{{store}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance region event", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 52, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_event_count{instance=\"$instance\", type=\"balance-leader-scheduler\"}[5m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "pd_scheduler_event_count", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance leader scheduler", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 53, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_event_count{instance=\"$instance\", type=\"balance-region-scheduler\"}[5m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "pd_scheduler_event_count", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Balance region scheduler", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 69, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_checker_event_count{instance=\"$instance\", type=\"namespace_checker\"}[1m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Namespace checker", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 70, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_checker_event_count{instance=\"$instance\", type=\"replica_checker\"}[1m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Replica checker", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 71, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_checker_event_count{instance=\"$instance\", type=\"merge_checker\"}[1m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region merge checker", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Scheduler", + "titleSize": "h4" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 45, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"create\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operator create", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 79, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"check\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operator check", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 77, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"finish\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operator finish", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 78, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"timeout\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operator timeout", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 80, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"canceled\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\", event=\"replaced\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operator replaced or canceled", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 0, + "id": 47, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_schedule_operators_count{instance=\"$instance\"}[1m])) by (event)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{event}}", + "metric": "pd_scheduler_status", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schedule operators count by state", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 67, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(pd_schedule_finish_operators_duration_seconds_bucket[5m])) by (type, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% operator finish duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 68, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.5, sum(rate(pd_schedule_finish_operators_duration_seconds_bucket[5m])) by (type, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "50% operator finish duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 81, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(pd_schedule_finish_operator_steps_duration_seconds_bucket[5m])) by (type, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% operator step finish duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "dtdurations", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 82, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.5, sum(rate(pd_schedule_finish_operator_steps_duration_seconds_bucket[5m])) by (type, le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "50% operator step finish duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "dtdurations", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Operator", + "titleSize": "h4" + }, + { + "collapse": true, + "height": "300px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(grpc_server_handling_seconds_count{instance=\"$instance\"}[1m])) by (grpc_method)", + "intervalFactor": 2, + "legendFormat": "{{grpc_method}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "completed commands rate", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 10, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 2, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(grpc_server_handling_seconds_bucket{instance=\"$instance\"}[5m])) by (grpc_method, le))", + "intervalFactor": 2, + "legendFormat": "{{grpc_method}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% completed_cmds_duration_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Grpc", + "titleSize": "h4" + }, + { + "collapse": true, + "height": "300px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 5, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_txn_handle_txns_duration_seconds_count[5m])) by (instance, result)", + "intervalFactor": 2, + "legendFormat": "{{instance}} : {{result}}", + "refId": "A", + "step": 2 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "handle_txns_count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 6, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(pd_txn_handle_txns_duration_seconds_bucket[5m])) by (instance, result, le))", + "intervalFactor": 2, + "legendFormat": "{{instance}} {{result}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% handle_txns_duration_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 7, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m])) by (instance, le))", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% wal_fsync_duration_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 34, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(etcd_network_peer_round_trip_time_seconds_bucket[5m])) by (instance, le))", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% peer_round_trip_time_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 0.1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "datasourceId": 1, + "model": { + "expr": "delta(etcd_disk_wal_fsync_duration_seconds_count[1m])", + "intervalFactor": 2, + "legendFormat": "{{instance}} etch disk wal fsync rate", + "refId": "A", + "step": 4 + }, + "params": [ + "A", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "60s", + "handler": 1, + "message": "PD etcd disk fsync maybe is down.", + "name": "etcd disk fsync", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 44, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "delta(etcd_disk_wal_fsync_duration_seconds_count[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} etch disk wal fsync rate", + "refId": "A", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 0.1 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "etcd disk wal fsync rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Etcd", + "titleSize": "h4" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 28, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_client_request_handle_requests_duration_seconds_count[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 2 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "handle_requests_count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 29, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "sort": "current", + "sortDesc": false, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.98, sum(rate(pd_client_request_handle_requests_duration_seconds_bucket[30s])) by (type, le))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{type}} 98th percentile", + "refId": "A", + "step": 2 + }, + { + "expr": "avg(rate(pd_client_request_handle_requests_duration_seconds_sum[30s])) by (type) / avg(rate(pd_client_request_handle_requests_duration_seconds_count[30s])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}} average", + "refId": "B", + "step": 2 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "handle_requests_duration_seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "TiDB", + "titleSize": "h4" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 54, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_region_heartbeat{instance=\"$instance\", type=\"report\", status=\"ok\"}[1m])) by (store)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region heartbeat report", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 77, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_region_heartbeat{instance=\"$instance\", type=\"report\", status=\"err\"}[1m])) by (store)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region heartbeat report error", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 78, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(pd_scheduler_region_heartbeat{instance=\"$instance\", type=\"report\", status=\"bind\"}[1m])) by (store)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region heartbeat report active", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 64, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_scheduler_region_heartbeat{type=\"push\",instance=\"$instance\"}[5m])*60) by (store, status)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "store{{store}}-{{status}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Region schedule push", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 74, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(pd_scheduler_region_heartbeat_latency_seconds_bucket[5m])) by (store, le))", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "store{{store}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% region heartbeat latency", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Heartbeat", + "titleSize": "h4" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "${DS_TEST-CLUSTER}", + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "instance", + "options": [], + "query": "label_values(pd_cluster_status, instance)", + "refresh": 1, + "regex": "", + "sort": 0, + "tagValuesQuery": null, + "tags": [], + "tagsQuery": null, + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": {}, + "datasource": "${DS_TEST-CLUSTER}", + "hide": 0, + "includeAll": true, + "label": "Namespace", + "multi": false, + "name": "namespace", + "options": [], + "query": "label_values(pd_cluster_status{instance=\"$instance\"}, namespace)", + "refresh": 1, + "regex": "", + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "Test-Cluster-PD", + "version": 3 +} diff --git a/docker/tidb/dashboard-installer/dashboards/tidb-dashboard-installer.sh b/docker/tidb/dashboard-installer/dashboards/tidb-dashboard-installer.sh new file mode 100755 index 0000000..826e26a --- /dev/null +++ b/docker/tidb/dashboard-installer/dashboards/tidb-dashboard-installer.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +url=$1 +userName=${GRAFANA_USERNAME:-admin} +password=${GRAFANA_PASSWORD:-admin} +datasource_url="http://${url}/api/datasources" +echo "Adding datasource..." +until curl -s -XPOST -H "Content-Type: application/json" --connect-timeout 1 -u ${userName}:${password} ${datasource_url} -d @/datasource.json >/dev/null; do + sleep 1 +done + +python grafana-config-copy.py "http://${url}/" ${userName} ${password} diff --git a/docker/tidb/dashboard-installer/dashboards/tidb.json b/docker/tidb/dashboard-installer/dashboards/tidb.json new file mode 100755 index 0000000..360be56 --- /dev/null +++ b/docker/tidb/dashboard-installer/dashboards/tidb.json @@ -0,0 +1,6928 @@ +{ + "__inputs": [ + { + "name": "DS_TEST-CLUSTER", + "label": "test-cluster", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "4.6.3" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "${DS_TEST-CLUSTER}", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "hideControls": false, + "id": null, + "links": [ + { + "icon": "doc", + "includeVars": true, + "keepTime": true, + "tags": [], + "targetBlank": true, + "title": "Report", + "tooltip": "Open a pdf report for the current dashboard", + "type": "link", + "url": "http://172.16.10.49:8686/api/report/test-cluster-tidb?apitoken=eyJrIjoiQkU2bmFQdjZMeTRmWVJ3UFo0bmV3Ym5zRG04Y2hmMHYiLCJuIjoiZ3JhZmFuYV9hcGlrZXkiLCJpZCI6MX0=" + } + ], + "refresh": "30s", + "rows": [ + { + "collapse": false, + "height": "240", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 80, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "95", + "refId": "C" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 42, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": false, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_server_query_total[1m])) by (result)", + "format": "time_series", + "instant": false, + "intervalFactor": 2, + "legendFormat": "query {{result}}", + "refId": "A", + "step": 60 + }, + { + "expr": "sum(rate(tidb_server_query_total{result=\"OK\"}[1m] offset 1d))", + "format": "time_series", + "hide": true, + "instant": false, + "intervalFactor": 3, + "legendFormat": "yesterday", + "refId": "B", + "step": 90 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "QPS", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 21, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_executor_statement_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Statement OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 2, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(tidb_server_query_total[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} {{type}} {{result}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "QPS By Instance", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 92, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_server_execute_error_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": " {{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Failed Query OPM", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Query Summary", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 23, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "B", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration 80 By Instance", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ instance }}", + "refId": "B", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration 95 By Instance", + "tooltip": { + "msResolution": true, + "shared": false, + "sort": 0, + "value_type": "cumulative" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + "max" + ] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 25, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration 99 By Instance", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 81, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration 999 By Instance", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 94, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "increase(tidb_server_execute_error_total[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}} @ {{instance}}", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Failed Query OPM Detail", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "description": "The internal SQL is used by TiDB itself.", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 68, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_session_restricted_sql_total[30s]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Internal SQL OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Query Detail", + "titleSize": "h6" + }, + { + "collapse": false, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 8, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "fill": 0, + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": true, + "targets": [ + { + "expr": "tidb_server_connections", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 40 + }, + { + "expr": "sum(tidb_server_connections)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "total", + "refId": "B", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Connection Count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 3, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "go_memstats_heap_inuse_bytes{job=~\"tidb.*\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "go_memstats_heap_inuse_bytes", + "refId": "B", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Heap Memory Usage", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 61, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": " go_goroutines{job=~\"tidb.*\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Goroutine Count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "description": "TiDB Server events.", + "fill": 1, + "id": 49, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "increase(tidb_server_event_total[10m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Events OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 54, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "increase(tidb_server_panic_total[1m])", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "panic-{{instance}}", + "refId": "A" + }, + { + "expr": "increase(tidb_server_critical_error_total[1m])", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "critical-{{instance}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Uncommon Error OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 82, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_monitor_keep_alive_total[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Keep Alive OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Server", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 69, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_session_transaction_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Transaction OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 72, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "95", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_session_transaction_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 74, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": " histogram_quantile(0.99, sum(rate(tidb_session_transaction_statement_num_bucket[30s])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "A" + }, + { + "expr": " histogram_quantile(0.80, sum(rate(tidb_session_transaction_statement_num_bucket[30s])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Transaction Statement Num", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 36, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_session_retry_error_total[30s]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "retry error", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Session Retry Error OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 67, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1.0, sum(rate(tidb_session_retry_num_bucket[30s])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "100", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_session_retry_num_bucket[30s])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_session_retry_num_bucket[30s])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Transaction Retry Num", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Transaction", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 301, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 76, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_executor_expensive_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Expensive Executors OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": null, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 91, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_server_plan_cache_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Queries Using Plan Cache OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Executor", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 12, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [ + { + "type": "dashboard" + } + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_distsql_handle_query_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999-{{type}}", + "refId": "D" + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_distsql_handle_query_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "99-{{type}}", + "metric": "tidb_distsql_handle_query_duration_seconds_bucket", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_distsql_handle_query_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90-{{type}}", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(tidb_distsql_handle_query_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "50-{{type}}", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Distsql Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.0005", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_distsql_handle_query_duration_seconds_count[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "metric": "tidb_distsql_query_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Distsql QPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 60, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_distsql_scan_keys_partial_num_count[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "metric": "tidb_distsql_query_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Distsql Partial QPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 57, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_distsql_scan_keys_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "100", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_distsql_scan_keys_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(tidb_distsql_scan_keys_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scan Keys Num", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 58, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_distsql_scan_keys_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "100", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_distsql_scan_keys_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_distsql_scan_keys_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scan Keys Partial Num", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 59, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_distsql_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "100", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_distsql_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(tidb_distsql_partial_num_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Partial Num", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 40, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "total", + "sortDesc": true, + "total": true, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_cop_actions_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Coprocessor Count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 41, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_tikvclient_cop_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Coprocessor Seconds 999", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Distsql", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 6, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tidb_tikvclient_backoff_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "9999", + "refId": "A", + "step": 40 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_tikvclient_backoff_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_tikvclient_backoff_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Retry Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 11, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_region_err_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tidb_server_session_execute_parse_duration_count", + "refId": "A", + "step": 40 + }, + { + "expr": "sum(rate(tidb_tikvclient_region_err_total{type=\"server_is_busy\"}[1m]))", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "sum", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "TiClient Region Error OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 53, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": true, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_backoff_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Backoff OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 32, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_lock_resolver_actions_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tidb_tikvclient_lock_resolver_actions_total", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Lock Resolve OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 84, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_lock_cleanup_task_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "cleanup_secondary_failure_{{type}}", + "metric": "tidb_tikvclient_lock_resolver_actions_total", + "refId": "A", + "step": 40 + }, + { + "expr": "sum(rate(tidb_tikvclient_load_safepoint_total{type=\"fail\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "load_safepoint_failure", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Other Errors OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "KV Errors", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 48, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tidb_tikvclient_request_seconds_bucket{type!=\"GC\"}[1m])) by (le, store))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "store-{{store}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Request Duration 999 by store", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 30, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_tikvclient_request_seconds_bucket{type!=\"GC\"}[1m])) by (le,type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Request Duration 999 by type", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 22, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(tidb_tikvclient_txn_cmd_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Cmd Duration 9999", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 18, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_tikvclient_txn_cmd_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Cmd Duration 99", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "KV Duration", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 5, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_txn_cmd_duration_seconds_count[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Cmd OPS", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 4, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_txn_total[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "KV Txn OPS", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 44, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_tikvclient_txn_regions_num_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Txn Regions Num 90", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 34, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "avg", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_tikvclient_txn_write_size_bytes_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Txn Write Size Bytes 100", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 33, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "avg", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_tikvclient_txn_write_kv_num_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "100 {{instance}}", + "refId": "B", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Txn Write KV Num 100", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 83, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "avg", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_load_safepoint_total{type=\"ok\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "B", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Load Safepoint OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "KV Count", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 20, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_client_cmd_handle_cmds_duration_seconds_count{type!=\"tso\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD Client CMD OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "description": "", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 35, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type!=\"tso\"}[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999-{{type}}", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type!=\"tso\"}[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99-{{type}}", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type!=\"tso\"}[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90-{{type}}", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD Client CMD Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 43, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_client_cmd_handle_failed_cmds_duration_seconds_count[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD Client CMD Fail OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "description": "The duration of a client calling GetTSAsync until received the TS result.", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 79, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(pd_client_cmd_handle_cmds_duration_seconds_count{type=\"tso\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "cmd", + "refId": "C" + }, + { + "expr": "sum(rate(pd_client_request_handle_requests_duration_seconds_count{type=\"tso\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "request", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD TSO OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "description": "The duration of a client calling GetTSAsync until received the TS result.", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 77, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(pd_client_cmd_handle_cmds_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD TSO Wait Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "description": "The duration of a client sending TSO request until received the response.", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 78, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.999, sum(rate(pd_client_request_handle_requests_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "999", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(pd_client_request_handle_requests_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(pd_client_request_handle_requests_duration_seconds_bucket{type=\"tso\"}[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD TSO RPC Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "PD Client", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 27, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_domain_load_schema_duration_seconds_bucket[1m])) by (le, instance))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Load Schema Duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 28, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "/.*failed/", + "bars": true + } + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_domain_load_schema_total[1m])) by (instance,type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{type}}", + "metric": "tidb_domain_load_schema_duration_count", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Load Schema OPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 29, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_session_schema_lease_error_total[1m])) by (instance)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "metric": "tidb_server_", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Schema Lease Error OPM", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Schema Load", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 9, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_ddl_handle_job_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "DDL Duration 95", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 63, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_ddl_batch_add_idx_succ_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Batch Add Index Duration 100", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 62, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "tidb_ddl_waiting_jobs", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "DDL Waiting Jobs Count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 55, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "increase(tidb_ddl_worker_operation_total[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "DDL OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 56, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(increase(tidb_ddl_worker_operation_duration_seconds_bucket[1m])) by (le, type, result))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}-{{result}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "DDL Worker Duration 99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 64, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_ddl_deploy_syncer_duration_seconds_bucket[2m])) by (le, type, result))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}-{{result}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Deploy Syncer Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 65, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_ddl_owner_handle_syncer_duration_seconds_bucket[2m])) by (le, type, result))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}-{{result}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Owner Handle Syncer Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 66, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1, sum(rate(tidb_ddl_update_self_ver_duration_seconds_bucket[2m])) by (le, result))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{result}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Update Self Version Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "DDL", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 46, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tidb_statistics_auto_analyze_duration_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "auto analyze duration", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Auto Analyze Duration 95", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 47, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_statistics_auto_analyze_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Auto Analyze QPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 70, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_statistics_stats_inaccuracy_rate_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99", + "refId": "A", + "step": 30 + }, + { + "expr": "histogram_quantile(0.90, sum(rate(tidb_statistics_stats_inaccuracy_rate_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(tidb_statistics_stats_inaccuracy_rate_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Stats Inaccuracy Rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 71, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_statistics_pseudo_estimation_total[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Pseudo Estimation OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 92, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_statistics_dump_feedback_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Dump Feedback OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 93, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_statistics_update_stats_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 30 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Update Stats OPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Statistics", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 50, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_autoid_operation_duration_seconds_count[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "AutoID QPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 51, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_autoid_operation_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99-{{type}}", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.80, sum(rate(tidb_autoid_operation_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "80-{{type}}", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "AutoID Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": "0.001", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 52, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_meta_operation_duration_seconds_bucket[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Meta Operations Duration 99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Meta", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 85, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_tikvclient_gc_worker_actions_total[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Worker Action OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 86, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tidb_tikvclient_gc_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Duration 99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 87, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "max(tidb_tikvclient_gc_config) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Config", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 88, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_tikvclient_gc_failure[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GC Failure OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 89, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_tikvclient_gc_action_result[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Action Result OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 90, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(tidb_tikvclient_gc_region_too_many_locks[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Too Many Locks Error OPM", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "GC", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "Test-Cluster-TiDB", + "version": 5 +} \ No newline at end of file diff --git a/docker/tidb/dashboard-installer/dashboards/tikv.json b/docker/tidb/dashboard-installer/dashboards/tikv.json new file mode 100755 index 0000000..e4ea020 --- /dev/null +++ b/docker/tidb/dashboard-installer/dashboards/tikv.json @@ -0,0 +1,14066 @@ +{ + "__inputs": [ + { + "name": "DS_TEST-CLUSTER", + "label": "test-cluster", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "4.6.3" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "singlestat", + "name": "Singlestat", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "${DS_TEST-CLUSTER}", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "hideControls": false, + "id": null, + "links": [ + { + "icon": "doc", + "includeVars": true, + "keepTime": true, + "tags": [], + "targetBlank": true, + "title": "Report", + "tooltip": "Open a pdf report for the current dashboard", + "type": "link", + "url": "http://172.16.10.49:8686/api/report/test-cluster-tikv?apitoken=eyJrIjoiQkU2bmFQdjZMeTRmWVJ3UFo0bmV3Ym5zRG04Y2hmMHYiLCJuIjoiZ3JhZmFuYV9hcGlrZXkiLCJpZCI6MX0=" + } + ], + "refresh": "1m", + "rows": [ + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 5, + "grid": {}, + "id": 56, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 0, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_size_bytes) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "store size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 5, + "grid": {}, + "id": 1706, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 0, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_store_size_bytes{type=\"available\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "available size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 5, + "grid": {}, + "id": 1707, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 0, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_store_size_bytes{type=\"capacity\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "capacity size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1708, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total[1m])) by (instance,job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}}-{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "cpu", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1709, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(process_resident_memory_bytes{job=~\"tikv.*\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "memory", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1710, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(node_disk_io_time_ms[1m]) / 1000", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{instance}} - {{device}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "io utilization", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1711, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"kv\", type=\"wal_file_bytes\"}[1m])) by (job)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}-write", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"kv\", type=~\"bytes_read|iter_bytes_read\"}[1m])) by (job)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}-read", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "MBps", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1713, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": false, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_duration_seconds_count{type!=\"kv_gc\"}[1m])) by (job,type)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}} - {{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "QPS", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1712, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_fail_total{type!=\"kv_gc\"}[1m])) by (job)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}-grpc-msg-fail", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(delta(tikv_pd_heartbeat_message_total{type=\"noop\"}[1m])) by (job) < 1", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}-pd-heartbeat", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Errps", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1715, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{type=\"leader\"}) by (job)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 10 + }, + { + "expr": "delta(tikv_pd_heartbeat_tick_total{type=\"leader\"}[30s]) < -10", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "leader", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1714, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{type=\"region\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "region", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Cluster", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1584, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_too_busy_total[1m])) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "scheduler-{{job}}", + "metric": "", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(rate(tikv_channel_full_total[1m])) by (job, type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "channelfull-{{job}}-{{type}}", + "metric": "", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_coprocessor_request_error{type='full'}[1m])) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "coprocessor-{{job}}", + "metric": "", + "refId": "C", + "step": 4 + }, + { + "expr": "avg(tikv_engine_write_stall{type=\"write_stall_percentile99\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "stall-{{job}}", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "server is busy", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "10s", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "10s", + "handler": 1, + "message": "TiKV server report failures", + "name": "server report failures alert", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 18, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_server_report_failure_msg_total[1m])) by (type,instance,job,store_id)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}} - {{type}} - to - {{store_id}}", + "metric": "tikv_server_raft_store_msg_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "server report failures", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1718, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_storage_engine_async_request_total{status!~\"success|all\"}[1m])) by (job, status)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}-{{status}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "raftstore error", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1719, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_stage_total{stage=~\"snapshot_err|prepare_write_err\"}[1m])) by (job, stage)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}-{{stage}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler error", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1720, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_request_error[1m])) by (job, reason)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}-{{reason}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor error", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1721, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_fail_total[1m])) by (job, type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}-{{type}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor error", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1722, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(tikv_pd_heartbeat_tick_total{type=\"leader\"}[1m])) by (job)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "leader drop", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1723, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_raftstore_leader_missing) by (job)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "leader missing", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Errors", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 34, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total", + "lines": false + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{type=\"leader\"}) by (job)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 10 + }, + { + "expr": "delta(tikv_pd_heartbeat_tick_total{type=\"leader\"}[30s]) < -10", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "leader", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 37, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_pd_heartbeat_tick_total{type=\"region\"}) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "region", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 3, + "grid": {}, + "id": 33, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_size_bytes) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "cf size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 5, + "grid": {}, + "id": 1705, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 0, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_size_bytes) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "store size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "datasourceId": 1, + "model": { + "expr": "sum(rate(tikv_channel_full_total[1m])) by (job, type)", + "intervalFactor": 2, + "legendFormat": "{{job}} - {{type}}", + "metric": "", + "refId": "A", + "step": 10 + }, + "params": [ + "A", + "10s", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "10s", + "handler": 1, + "message": "TiKV channel full", + "name": "TiKV channel full alert", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 3, + "grid": {}, + "id": 22, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_channel_full_total[1m])) by (job, type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}} - {{type}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "channel full", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1717, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_server_report_failure_msg_total[1m])) by (type,instance,job,store_id)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}} - {{type}} - to - {{store_id}}", + "metric": "tikv_server_raft_store_msg_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "server report failures", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 57, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_region_written_keys_sum[1m])) by (job) / sum(rate(tikv_region_written_keys_count[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_region_written_keys_bucket", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "region average written keys", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 58, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_region_written_bytes_sum[1m])) by (job) / sum(rate(tikv_region_written_bytes_count[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_regi", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "region average written bytes", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 75, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_region_written_keys_count[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_region_written_keys_bucket", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "active written leaders", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1481, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_region_size_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_region_size_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_raftstore_region_size_sum[1m])) / sum(rate(tikv_raftstore_region_size_count[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "approximate region size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Server", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 31, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_apply_log_duration_seconds_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " 99%", + "metric": "", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_apply_log_duration_seconds_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_raftstore_apply_log_duration_seconds_sum[1m])) / sum(rate(tikv_raftstore_apply_log_duration_seconds_count[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "apply log duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 32, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_apply_log_duration_seconds_bucket[1m])) by (le, job))", + "intervalFactor": 2, + "legendFormat": " {{job}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "apply log duration per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 39, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_append_log_duration_seconds_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " 99%", + "metric": "", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_append_log_duration_seconds_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_raftstore_append_log_duration_seconds_sum[1m])) / sum(rate(tikv_raftstore_append_log_duration_seconds_count[1m]))", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "append log duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 40, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_append_log_duration_seconds_bucket[1m])) by (le, job))", + "intervalFactor": 2, + "legendFormat": "{{job}} ", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "append log duration per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Raft IO", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 5, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_raft_ready_handled_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_raftstore_raft_ready_handled_total", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(rate(tikv_raftstore_raft_process_duration_secs_count{type=\"ready\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "count", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "ready handled", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 118, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_raft_process_duration_secs_bucket{type='ready'}[1m])) by (le, job))", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "C", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 1 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "process ready duration per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1165, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_raft_process_duration_secs_bucket{type='tick'}[1m])) by (le, job))", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "C", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 1 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "process tick duration per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Raft Process", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1615, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_raft_sent_message_total[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "sent messages per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1616, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_server_raft_message_flush_total[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_server_raft_message_flush_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "flush messages per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 106, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_server_raft_message_recv_total[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "recv messages per server", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 11, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_raft_sent_message_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "messages", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 25, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_raft_sent_message_total{type=\"vote\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "vote", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1309, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_raft_dropped_message_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "raft dropped messages", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Raft Message", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 108, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_apply_proposal_bucket[1m])) by (le, job))", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "raft proposals per ready", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 7, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_proposal_total{type=~\"local_read|normal|read_index\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_raftstore_proposal_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "raft read/write proposals", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 119, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_proposal_total{type=~\"local_read|read_index\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "raft read proposals per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 120, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_proposal_total{type=\"normal\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_raftstore_proposal_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "raft write proposals per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 41, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_request_wait_time_duration_secs_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "tikv_raftstore_request_wait_time_duration_secs_bucket", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_request_wait_time_duration_secs_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_raftstore_request_wait_time_duration_secs_sum[1m])) / sum(rate(tikv_raftstore_request_wait_time_duration_secs_count[1m]))", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "propose wait duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 42, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_request_wait_time_duration_secs_bucket[1m])) by (le, job))", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "propose wait duration per server", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 1975, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": true, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(rate(tikv_raftstore_propose_log_size_sum[1m])) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "raft log speed", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "short", + "label": "bytes/s", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Raft Propose", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 76, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_proposal_total{type=~\"conf_change|transfer_leader\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_raftstore_proposal_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "admin proposals", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 77, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_admin_cmd_total{status=\"success\", type!=\"compact\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_raftstore_admin_cmd_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "admin apply", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 70, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_raftstore_check_split_total{type!=\"ignore\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_raftstore_check_split_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "check split", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 71, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tikv_raftstore_check_split_duration_seconds_bucket[1m])) by (le, job))", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_raftstore_check_split_duration_seconds_bucket", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99.99% check split duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Raft Admin", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 2, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_storage_command_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "storage command total", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 8, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_storage_engine_async_request_total{status!~\"all|success\"}[1m])) by (status)", + "intervalFactor": 2, + "legendFormat": "{{status}}", + "metric": "tikv_raftstore_raft_process_duration_secs_bucket", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "storage async request error", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 15, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{type=\"snapshot\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{type=\"snapshot\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_engine_async_request_duration_seconds_sum{type=\"snapshot\"}[1m])) / sum(rate(tikv_storage_engine_async_request_duration_seconds_count{type=\"snapshot\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "storage async snapshot duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 109, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{type=\"write\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{type=\"write\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_engine_async_request_duration_seconds_sum{type=\"write\"}[1m])) / sum(rate(tikv_storage_engine_async_request_duration_seconds_count{type=\"write\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "storage async write duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1310, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "raft-95%", + "yaxis": 2 + } + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_storage_batch_commands_total_bucket[30s])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_storage_batch_commands_total_bucket[30s])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "B", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_batch_commands_total_sum[30s])) / sum(rate(tikv_storage_batch_commands_total_count[30s]))", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_raftstore_batch_snapshot_commands_total_bucket[30s])) by (le))", + "intervalFactor": 2, + "legendFormat": "raft-95%", + "refId": "D", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "storage async batch snapshot", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "Storage Batch Size", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": "Raftstore Batch Size", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Storage", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "height": "400", + "id": 167, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_too_busy_total[1m])) by (stage)", + "intervalFactor": 2, + "legendFormat": "busy", + "refId": "A", + "step": 20 + }, + { + "expr": "sum(rate(tikv_scheduler_stage_total[1m])) by (stage)", + "intervalFactor": 2, + "legendFormat": "{{stage}}", + "refId": "B", + "step": 20 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler stage total", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "height": "", + "id": 1, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": 6, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_commands_pri_total[1m])) by (priority)", + "intervalFactor": 2, + "legendFormat": "{{priority}}", + "metric": "", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler priority commands", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 300 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "120s", + "handler": 1, + "message": "TiKV scheduler context total", + "name": "scheduler pending commands alert", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "height": "", + "id": 193, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": 6, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_scheduler_contex_total) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "", + "refId": "A", + "step": 40 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 300 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "scheduler pending commands", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Scheduler", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "height": "400", + "id": 168, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 12, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_too_busy_total{type=\"$command\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "busy", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(rate(tikv_scheduler_stage_total{type=\"$command\"}[1m])) by (stage)", + "intervalFactor": 2, + "legendFormat": "{{stage}}", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler stage total", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 3, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_scheduler_command_duration_seconds_bucket{type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_scheduler_command_duration_seconds_bucket{type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_scheduler_command_duration_seconds_sum{type=\"$command\"}[1m])) / sum(rate(tikv_scheduler_command_duration_seconds_count{type=\"$command\"}[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler command duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 194, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_scheduler_latch_wait_duration_seconds_bucket{type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_scheduler_latch_wait_duration_seconds_bucket{type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_scheduler_latch_wait_duration_seconds_sum{type=\"$command\"}[1m])) / sum(rate(tikv_scheduler_latch_wait_duration_seconds_count{type=\"$command\"}[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler latch wait duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 195, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_scheduler_kv_command_key_read_bucket{type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "kv_command_key", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_scheduler_kv_command_key_read_bucket{type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_scheduler_kv_command_key_read_sum{type=\"$command\"}[1m])) / sum(rate(tikv_scheduler_kv_command_key_read_count{type=\"$command\"}[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler keys read", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 373, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_scheduler_kv_command_key_write_bucket{type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "kv_command_key", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_scheduler_kv_command_key_write_bucket{type=\"$command\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_scheduler_kv_command_key_write_sum{type=\"$command\"}[1m])) / sum(rate(tikv_scheduler_kv_command_key_write_count{type=\"$command\"}[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler keys written", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 560, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_kv_scan_details{req=\"$command\"}[1m])) by (tag)", + "intervalFactor": 2, + "legendFormat": "{{tag}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler scan details", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 675, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_kv_scan_details{req=\"$command\", cf=\"lock\"}[1m])) by (tag)", + "intervalFactor": 2, + "legendFormat": "{{tag}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler scan details [lock]", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 829, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_kv_scan_details{req=\"$command\", cf=\"write\"}[1m])) by (tag)", + "intervalFactor": 2, + "legendFormat": "{{tag}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler scan details [write]", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 830, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_scheduler_kv_scan_details{req=\"$command\", cf=\"default\"}[1m])) by (tag)", + "intervalFactor": 2, + "legendFormat": "{{tag}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler scan details [default]", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": "command", + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Scheduler - $command", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 5, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 16, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_coprocessor_request_duration_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_duration_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-95%", + "refId": "B", + "step": 4 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_duration_seconds_sum{req=\"select\"}[1m])) / sum(rate(tikv_coprocessor_request_duration_seconds_count{req=\"select\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "select-avg", + "refId": "C", + "step": 4 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_duration_seconds_sum{req=\"index\"}[1m])) / sum(rate(tikv_coprocessor_request_duration_seconds_count{req=\"index\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "index-avg", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor request duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 5, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 111, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_coprocessor_request_wait_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_wait_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-95%", + "refId": "B", + "step": 4 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_wait_seconds_sum[1m])) / sum(rate(tikv_coprocessor_request_wait_seconds_count[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "C", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor wait duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 5, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 113, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_coprocessor_request_handle_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-99%", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_handle_seconds_bucket[1m])) by (le,req))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{req}}-95%", + "refId": "B", + "step": 4 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_handle_seconds_sum{req=\"select\"}[1m])) / sum(rate(tikv_coprocessor_request_handle_seconds_count{req=\"select\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "select-avg", + "refId": "C", + "step": 4 + }, + { + "expr": " sum(rate(tikv_coprocessor_request_handle_seconds_sum{req=\"index\"}[1m])) / sum(rate(tikv_coprocessor_request_handle_seconds_count{req=\"index\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "index-avg", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor handle duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 5, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 115, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_duration_seconds_bucket[1m])) by (le, job,req))", + "intervalFactor": 2, + "legendFormat": "{{job}}-{{req}}", + "metric": "tikv_coprocessor_request_duration_seconds_bucket", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "95% coprocessor request duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 5, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 116, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_wait_seconds_bucket[1m])) by (le, job,req))", + "intervalFactor": 2, + "legendFormat": "{{job}}-{{req}}", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "95% coprocessor wait duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 5, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 117, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_coprocessor_request_handle_seconds_bucket[1m])) by (le, job,req))", + "intervalFactor": 2, + "legendFormat": "{{job}}-{{req}}", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "95% coprocessor handle duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 52, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, avg(rate(tikv_coprocessor_scan_keys_bucket[1m])) by (le, req)) ", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{req}}-99%", + "metric": "tikv_coprocessor_scan_keys_bucket", + "refId": "A", + "step": 10 + }, + { + "expr": "histogram_quantile(0.95, avg(rate(tikv_coprocessor_scan_keys_bucket[1m])) by (le, req)) ", + "intervalFactor": 2, + "legendFormat": "{{req}}-95%", + "metric": "tikv_coprocessor_scan_keys_bucket", + "refId": "B", + "step": 10 + }, + { + "expr": "histogram_quantile(0.90, avg(rate(tikv_coprocessor_scan_keys_bucket[1m])) by (le, req)) ", + "intervalFactor": 2, + "legendFormat": "{{req}}-90%", + "metric": "tikv_coprocessor_scan_keys_bucket", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor scan keys", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 551, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_executor_count[1m])) by (type)", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor executor count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 74, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_request_error[1m])) by (reason)", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{reason}}", + "metric": "tikv_coprocessor_request_error", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor request errors", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 552, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_scan_details[1m])) by (tag,req)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{req}}-{{tag}}", + "metric": "scan_details", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor scan details", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 122, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_scan_details{req=\"select\"}[1m])) by (tag,cf)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{cf}}-{{tag}}", + "metric": "scan_details", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor table scan details -- cf", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 554, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": "cf", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_scan_details{req=\"index\"}[1m])) by (tag,cf)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{cf}}-{{tag}}", + "metric": "scan_details", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor index scan details - cf", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Coprocessor", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 26, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1.0, sum(rate(tikv_storage_mvcc_versions_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " max", + "metric": "", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_storage_mvcc_versions_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "B", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_storage_mvcc_versions_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " 95%", + "metric": "", + "refId": "C", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_mvcc_versions_sum[1m])) / sum(rate(tikv_storage_mvcc_versions_count[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "D", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "MVCC Versions", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 559, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1.0, sum(rate(tikv_storage_mvcc_gc_delete_versions_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " max", + "metric": "", + "refId": "A", + "step": 4 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_storage_mvcc_gc_delete_versions_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "B", + "step": 4 + }, + { + "expr": "histogram_quantile(0.95, sum(rate(tikv_storage_mvcc_gc_delete_versions_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": " 95%", + "metric": "", + "refId": "C", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_mvcc_gc_delete_versions_sum[1m])) / sum(rate(tikv_storage_mvcc_gc_delete_versions_count[1m])) ", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "D", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "MVCC Delete Versions", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 121, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_storage_command_total{type=\"gc\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "total", + "metric": "tikv_storage_command_total", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(rate(tikv_storage_gc_skipped_counter[1m]))", + "intervalFactor": 2, + "legendFormat": "skipped", + "metric": "tikv_storage_gc_skipped_counter", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GC Commands", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 967, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_gc_action_result_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GC Actions Result", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 966, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_gc_worker_actions_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GC Worker Actions", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 969, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(1.0, sum(rate(tidb_tikvclient_gc_seconds_bucket[1m])) by (instance, le))", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "gc Seconds", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 968, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tidb_tikvclient_gc_failure_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "gc failure", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 2, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 0, + "editable": true, + "error": false, + "format": "s", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 27, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 3, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "max(tidb_tikvclient_gc_config{type=\"tikv_gc_life_time\"})", + "interval": "", + "intervalFactor": 2, + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "GC LifeTime", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 0, + "editable": true, + "error": false, + "format": "s", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "id": 28, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "null", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 3, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "targets": [ + { + "expr": "max(tidb_tikvclient_gc_config{type=\"tikv_gc_run_interval\"})", + "intervalFactor": 2, + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "GC interval", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "GC", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 35, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(delta(tikv_raftstore_raft_sent_message_total{type=\"snapshot\"}[1m]))", + "intervalFactor": 2, + "legendFormat": " ", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "rate snapshot message", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 36, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_server_send_snapshot_duration_seconds_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "send", + "refId": "A", + "step": 60 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_snapshot_duration_seconds_bucket{type=\"apply\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "apply", + "refId": "B", + "step": 60 + }, + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_raftstore_snapshot_duration_seconds_bucket{type=\"generate\"}[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "generate", + "refId": "C", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% handle snapshot duration", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 38, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 4, + "stack": false, + "steppedLine": true, + "targets": [ + { + "expr": "sum(tikv_raftstore_snapshot_traffic_total) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "snapshot state count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 44, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tikv_snapshot_size_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "size", + "metric": "tikv_snapshot_size_bucket", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99.99% snapshot size", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 43, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9999, sum(rate(tikv_snapshot_kv_count_bucket[1m])) by (le))", + "intervalFactor": 2, + "legendFormat": "count", + "metric": "tikv_snapshot_kv_count_bucket", + "refId": "A", + "step": 40 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99.99% snapshot kv count", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Snapshot", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 59, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 400, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_worker_handled_task_total[1m])) by (name)", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Worker Handled Tasks", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1395, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 400, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_worker_pending_task_total[1m])) by (name)", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Worker Pending Tasks", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1876, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 400, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_futurepool_handled_task_total[1m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "FuturePool Handled Tasks", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1877, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 400, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_futurepool_pending_task_total[1m])) by (name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{name}}", + "metric": "tikv_pd_heartbeat_tick_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "FuturePool Pending Tasks", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 2 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "10s", + "handler": 1, + "message": "coprocessor pending requests", + "name": "coprocessor pending requests alert", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 2, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 550, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_coprocessor_pending_request[1m])) by (req, priority)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{ req }} - {{priority}}", + "metric": "tikv_coprocessor_request_error", + "refId": "A", + "step": 4 + }, + { + "expr": "sum(rate(tikv_coprocessor_pending_request[1m])) BY (type, instance)", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 2 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "coprocessor pending requests", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Task", + "titleSize": "h6" + }, + { + "collapse": true, + "height": 250, + "panels": [ + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 0.8 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "datasourceId": 1, + "model": { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"raftstore_.*\"}[1m])) by (job, name)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 20 + }, + "params": [ + "A", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "60s", + "handler": 1, + "message": "TiKV raftstore thread CPU usage is high", + "name": "TiKV raft store CPU alert", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 61, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"raftstore_.*\"}[1m])) by (job, name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0.8 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "raft store CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 79, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=\"apply_worker\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "async apply CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 63, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"storage_schedul.*\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "scheduler CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 64, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"sched_.*\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scheduler Worker CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 1908, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"store_read.*\"}[1m])) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Storage ReadPool CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 78, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"cop_.*\"}[1m])) by (job)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Coprocessor CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 67, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"snapshot_worker.*\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "snapshot worker CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 68, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"split_check.*\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "split check CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "editable": true, + "error": false, + "fill": 0, + "grid": {}, + "id": 69, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "max(rate(tikv_thread_cpu_seconds_total{name=~\"rocksdb.*\"}[1m])) by (job)", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_thread_cpu_seconds_total", + "refId": "A", + "step": 4 + } + ], + "thresholds": [ + { + "colorMode": "warning", + "fill": true, + "line": true, + "op": "gt", + "value": 1 + }, + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 4 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "rocksdb CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 105, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 250, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_thread_cpu_seconds_total{name=~\"grpc.*\"}[1m])) by (job)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{job}}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "grpc poll CPU", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Thread CPU", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 138, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_memtable_efficiency{db=\"$db\", type=\"memtable_hit\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "memtable", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=~\"block_cache_data_hit|block_cache_filter_hit\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "block_cache", + "metric": "", + "refId": "E", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_get_served{db=\"$db\", type=\"get_hit_l0\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "l0", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_get_served{db=\"$db\", type=\"get_hit_l1\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "l1", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_get_served{db=\"$db\", type=\"get_hit_l2_and_up\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "l2_and_up", + "refId": "F", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Get Operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 82, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_get_micro_seconds{db=\"$db\",type=\"get_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_get_micro_seconds{db=\"$db\",type=\"get_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_get_micro_seconds{db=\"$db\",type=\"get_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_get_micro_seconds{db=\"$db\",type=\"get_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Get Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 129, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_locate{db=\"$db\", type=\"number_db_seek\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "seek", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_locate{db=\"$db\", type=\"number_db_seek_found\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "seek_found", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_locate{db=\"$db\", type=\"number_db_next\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "next", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_locate{db=\"$db\", type=\"number_db_next_found\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "next_found", + "metric": "", + "refId": "D", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_locate{db=\"$db\", type=\"number_db_prev\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "prev", + "metric": "", + "refId": "E", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_locate{db=\"$db\", type=\"number_db_prev_found\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "prev_found", + "metric": "", + "refId": "F", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Seek Operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 125, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_seek_micro_seconds{db=\"$db\",type=\"seek_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_seek_micro_seconds{db=\"$db\",type=\"seek_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_seek_micro_seconds{db=\"$db\",type=\"seek_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_seek_micro_seconds{db=\"$db\",type=\"seek_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Seek Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 139, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_write_served{db=\"$db\", type=~\"write_done_by_self|write_done_by_other\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "done", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_write_served{db=\"$db\", type=\"write_timeout\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "timeout", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_write_served{db=\"$db\", type=\"write_with_wal\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "with_wal", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Write Operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 126, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_write_micro_seconds{db=\"$db\",type=\"write_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_micro_seconds{db=\"$db\",type=\"write_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_micro_seconds{db=\"$db\",type=\"write_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_micro_seconds{db=\"$db\",type=\"write_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Write Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 137, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_wal_file_synced{db=\"$db\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "sync", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "WAL Sync Operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 135, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 6, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_wal_file_sync_micro_seconds{db=\"$db\",type=\"wal_file_sync_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_wal_file_sync_micro_seconds{db=\"$db\",type=\"wal_file_sync_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_wal_file_sync_micro_seconds{db=\"$db\",type=\"wal_file_sync_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_wal_file_sync_micro_seconds{db=\"$db\",type=\"wal_file_sync_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "WAL Sync Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 128, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_event_total{db=\"$db\"}[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_engine_event_total", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compaction Operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 136, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": null, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_compaction_time{db=\"$db\",type=\"compaction_time_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_compaction_time{db=\"$db\",type=\"compaction_time_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_compaction_time{db=\"$db\",type=\"compaction_time_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_compaction_time{db=\"$db\",type=\"compaction_time_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compaction Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 140, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_sst_read_micros{db=\"$db\", type=\"sst_read_micros_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_sst_read_micros{db=\"$db\", type=\"sst_read_micros_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_sst_read_micros{db=\"$db\", type=\"sst_read_micros_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_sst_read_micros{db=\"$db\", type=\"sst_read_micros_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "SST Read Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 10, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 87, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_write_stall{db=\"$db\", type=\"write_stall_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_stall{db=\"$db\", type=\"write_stall_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_stall{db=\"$db\", type=\"write_stall_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_write_stall{db=\"$db\", type=\"write_stall_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "metric": "", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Write Stall Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 10, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 103, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_memory_bytes{db=\"$db\", type=\"mem-tables\"}) by (cf)", + "intervalFactor": 2, + "legendFormat": "{{cf}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memtable Size", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 88, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": null, + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_memtable_efficiency{db=\"$db\", type=\"memtable_hit\"}[1m])) / (sum(rate(tikv_engine_memtable_efficiency{db=\"$db\", type=\"memtable_hit\"}[1m])) + sum(rate(tikv_engine_memtable_efficiency{db=\"$db\", type=\"memtable_miss\"}[1m])))", + "intervalFactor": 2, + "legendFormat": "hit", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memtable Hit", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 102, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_block_cache_size_bytes{db=\"$db\"}) by(cf)", + "intervalFactor": 2, + "legendFormat": "{{cf}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Block Cache Size", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 80, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 6, + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_hit\"}[1m])) / (sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_hit\"}[1m])) + sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_miss\"}[1m])))", + "intervalFactor": 2, + "legendFormat": "all", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_data_hit\"}[1m])) / (sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_data_hit\"}[1m])) + sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_data_miss\"}[1m])))", + "intervalFactor": 2, + "legendFormat": "data", + "metric": "", + "refId": "D", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_filter_hit\"}[1m])) / (sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_filter_hit\"}[1m])) + sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_filter_miss\"}[1m])))", + "intervalFactor": 2, + "legendFormat": "filter", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_index_hit\"}[1m])) / (sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_index_hit\"}[1m])) + sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_index_miss\"}[1m])))", + "intervalFactor": 2, + "legendFormat": "index", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_bloom_efficiency{db=\"$db\", type=\"bloom_prefix_useful\"}[1m])) / sum(rate(tikv_engine_bloom_efficiency{db=\"$db\", type=\"bloom_prefix_checked\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "bloom prefix", + "metric": "", + "refId": "E", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Block Cache Hit", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "height": "", + "id": 467, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"block_cache_byte_read\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "total_read", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"block_cache_byte_write\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "total_written", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_data_bytes_insert\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "data_insert", + "metric": "", + "refId": "D", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_filter_bytes_insert\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "filter_insert", + "metric": "", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_filter_bytes_evict\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "filter_evict", + "metric": "", + "refId": "E", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_index_bytes_insert\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "index_insert", + "metric": "", + "refId": "F", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_index_bytes_evict\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "index_evict", + "metric": "", + "refId": "G", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Block Cache Flow", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 468, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_add\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "total_add", + "metric": "", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_data_add\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "data_add", + "metric": "", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_filter_add\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "filter_add", + "metric": "", + "refId": "D", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_index_add\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "index_add", + "metric": "", + "refId": "E", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_cache_efficiency{db=\"$db\", type=\"block_cache_add_failures\"}[1m]))", + "intervalFactor": 2, + "legendFormat": "add_failures", + "metric": "", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Block Cache Operations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "height": "", + "id": 132, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"keys_read\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "read", + "refId": "B", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"keys_written\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "written", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_compaction_num_corrupt_keys{db=\"$db\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "corrupt", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Keys Flow", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 131, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(tikv_engine_estimate_num_keys{db=\"$db\"}) by (cf)", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{cf}}", + "metric": "tikv_engine_estimate_num_keys", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Total Keys", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "height": "", + "id": 85, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"bytes_read\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "get", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"iter_bytes_read\"}[1m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "scan", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Read Flow", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 133, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 6, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_bytes_per_read{db=\"$db\",type=\"bytes_per_read_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_read{db=\"$db\",type=\"bytes_per_read_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_read{db=\"$db\",type=\"bytes_per_read_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_read{db=\"$db\",type=\"bytes_per_read_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Bytes / Read", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "height": "", + "id": 86, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"wal_file_bytes\"}[1m]))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "wal", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"bytes_written\"}[1m]))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "write", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Write Flow", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 0, + "id": 134, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "minSpan": 6, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_bytes_per_write{db=\"$db\",type=\"bytes_per_write_max\"})", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "A", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_write{db=\"$db\",type=\"bytes_per_write_percentile99\"})", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "B", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_write{db=\"$db\",type=\"bytes_per_write_percentile95\"})", + "intervalFactor": 2, + "legendFormat": "95%", + "refId": "C", + "step": 10 + }, + { + "expr": "avg(tikv_engine_bytes_per_write{db=\"$db\",type=\"bytes_per_write_average\"})", + "intervalFactor": 2, + "legendFormat": "avg", + "refId": "D", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Bytes / Write", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 10, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 90, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_compaction_flow_bytes{db=\"$db\", type=\"bytes_read\"}[1m]))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "read", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_compaction_flow_bytes{db=\"$db\", type=\"bytes_written\"}[1m]))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "written", + "refId": "C", + "step": 10 + }, + { + "expr": "sum(rate(tikv_engine_flow_bytes{db=\"$db\", type=\"flush_write_bytes\"}[1m]))", + "hide": false, + "intervalFactor": 2, + "legendFormat": "flushed", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compaction Flow", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 127, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_pending_compaction_bytes{db=\"$db\"}[1m])) by (cf)", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{cf}}", + "metric": "tikv_engine_pending_compaction_bytes", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compaction Pending Bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 518, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_engine_read_amp_flow_bytes{db=\"$db\", type=\"read_amp_total_read_bytes\"}[1m])) by (job) / sum(rate(tikv_engine_read_amp_flow_bytes{db=\"$db\", type=\"read_amp_estimate_useful_bytes\"}[1m])) by (job)", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Read Amplication", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 863, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_compression_ratio{db=\"$db\"}) by (level)", + "hide": false, + "intervalFactor": 2, + "legendFormat": "level - {{level}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compression Ratio", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 516, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "tikv_engine_num_snapshots{db=\"$db\"}", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Number of Snapshots", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 517, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "tikv_engine_oldest_snapshot_duration{db=\"$db\"}", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{job}}", + "metric": "tikv_engine_oldest_snapshot_duration", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Oldest Snapshots Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 2002, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": true, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(tikv_engine_num_files_at_level{db=\"$db\"}) by (cf, level)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "cf-{{cf}}, level-{{level}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Number files at each level", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "fill": 1, + "id": 2003, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": true, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_snapshot_ingest_sst_duration_seconds_bucket[1m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A" + }, + { + "expr": "sum(rate(tikv_snapshot_ingest_sst_duration_seconds_sum[1m])) / sum(rate(tikv_snapshot_ingest_sst_duration_seconds_count[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "average", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Ingest SST duration seconds", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": "db", + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Rocksdb - $db", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 95, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_duration_seconds_count{type!=\"kv_gc\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_grpc_msg_duration_seconds_bucket", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "grpc message count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 107, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_fail_total{type!=\"kv_gc\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "metric": "tikv_grpc_msg_fail_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "grpc message failed", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 98, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_grpc_msg_duration_seconds_bucket{type!=\"kv_gc\"}[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% grpc messge duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 10, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 1844, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_grpc_msg_duration_seconds_count{type=\"kv_gc\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "gc message cnt", + "metric": "tikv_grpc_msg_duration_seconds_bucket", + "refId": "A", + "step": 10 + }, + { + "expr": "sum(rate(tikv_grpc_msg_fail_total{type=\"kv_gc\"}[1m])) by (type)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "gc failed cnt", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "grpc gc message count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 1845, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 300, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(tikv_grpc_msg_duration_seconds_bucket{type=\"kv_gc\"}[1m])) by (le, type))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "99% grpc kv_gc messge duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 10, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Grpc", + "titleSize": "h6" + }, + { + "collapse": true, + "height": "300", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 1069, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 350, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_pd_request_duration_seconds_count[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{ type }}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 1070, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 350, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_pd_request_duration_seconds_sum[1m])) by (type) / sum(rate(tikv_pd_request_duration_seconds_count[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{ type }}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD request duration (average)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 1215, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 350, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_pd_heartbeat_message_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{ type }}", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD heartbeats", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "opm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TEST-CLUSTER}", + "decimals": 1, + "fill": 1, + "id": 1396, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": 350, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(tikv_pd_validate_peer_total[1m])) by (type)", + "intervalFactor": 2, + "legendFormat": "{{ type }}", + "metric": "", + "refId": "A", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "PD validate peers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "PD", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "${DS_TEST-CLUSTER}", + "hide": 0, + "includeAll": true, + "label": "db", + "multi": true, + "name": "db", + "options": [], + "query": "label_values(tikv_engine_block_cache_size_bytes, db)", + "refresh": 1, + "regex": "", + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_TEST-CLUSTER}", + "hide": 0, + "includeAll": true, + "label": "command", + "multi": true, + "name": "command", + "options": [], + "query": "label_values(tikv_storage_command_total, type)", + "refresh": 1, + "regex": "", + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "Test-Cluster-TiKV", + "version": 2 +} diff --git a/docker/tidb/docker-compose-binlog.yml b/docker/tidb/docker-compose-binlog.yml new file mode 100755 index 0000000..b0369b4 --- /dev/null +++ b/docker/tidb/docker-compose-binlog.yml @@ -0,0 +1,448 @@ +--- +# Source: tidb-docker-compose/templates/docker-compose.yml +# dashboard-installer has been deleted, because we don't need it any more in docker-compose model +version: '2.1' + +services: + pd0: + image: pingcap/pd:latest + ports: + - "2379" + volumes: + - ./config/pd.toml:/pd.toml:ro + - ./data:/data + - ./logs:/logs + command: + - --name=pd0 + - --client-urls=http://0.0.0.0:2379 + - --peer-urls=http://0.0.0.0:2380 + - --advertise-client-urls=http://pd0:2379 + - --advertise-peer-urls=http://pd0:2380 + - --initial-cluster=pd0=http://pd0:2380,pd1=http://pd1:2380,pd2=http://pd2:2380 + - --data-dir=/data/pd0 + - --config=/pd.toml + - --log-file=/logs/pd0.log + # sysctls: + # net.core.somaxconn: 32768 + # ulimits: + # nofile: + # soft: 1000000 + # hard: 1000000 + restart: on-failure + + pd1: + image: pingcap/pd:latest + ports: + - "2379" + volumes: + - ./config/pd.toml:/pd.toml:ro + - ./data:/data + - ./logs:/logs + command: + - --name=pd1 + - --client-urls=http://0.0.0.0:2379 + - --peer-urls=http://0.0.0.0:2380 + - --advertise-client-urls=http://pd1:2379 + - --advertise-peer-urls=http://pd1:2380 + - --initial-cluster=pd0=http://pd0:2380,pd1=http://pd1:2380,pd2=http://pd2:2380 + - --data-dir=/data/pd1 + - --config=/pd.toml + - --log-file=/logs/pd1.log + # sysctls: + # net.core.somaxconn: 32768 + # ulimits: + # nofile: + # soft: 1000000 + # hard: 1000000 + restart: on-failure + + pd2: + image: pingcap/pd:latest + ports: + - "2379" + volumes: + - ./config/pd.toml:/pd.toml:ro + - ./data:/data + - ./logs:/logs + command: + - --name=pd2 + - --client-urls=http://0.0.0.0:2379 + - --peer-urls=http://0.0.0.0:2380 + - --advertise-client-urls=http://pd2:2379 + - --advertise-peer-urls=http://pd2:2380 + - --initial-cluster=pd0=http://pd0:2380,pd1=http://pd1:2380,pd2=http://pd2:2380 + - --data-dir=/data/pd2 + - --config=/pd.toml + - --log-file=/logs/pd2.log + # sysctls: + # net.core.somaxconn: 32768 + # ulimits: + # nofile: + # soft: 1000000 + # hard: 1000000 + restart: on-failure + + tikv0: + image: pingcap/tikv:latest + volumes: + - ./config/tikv.toml:/tikv.toml:ro + - ./data:/data + - ./logs:/logs + command: + - --addr=0.0.0.0:20160 + - --advertise-addr=tikv0:20160 + - --data-dir=/data/tikv0 + - --pd=pd0:2379,pd1:2379,pd2:2379 + - --config=/tikv.toml + - --log-file=/logs/tikv0.log + depends_on: + - "pd0" + - "pd1" + - "pd2" + # sysctls: + # net.core.somaxconn: 32768 + # ulimits: + # nofile: + # soft: 1000000 + # hard: 1000000 + restart: on-failure + + tikv1: + image: pingcap/tikv:latest + volumes: + - ./config/tikv.toml:/tikv.toml:ro + - ./data:/data + - ./logs:/logs + command: + - --addr=0.0.0.0:20160 + - --advertise-addr=tikv1:20160 + - --data-dir=/data/tikv1 + - --pd=pd0:2379,pd1:2379,pd2:2379 + - --config=/tikv.toml + - --log-file=/logs/tikv1.log + depends_on: + - "pd0" + - "pd1" + - "pd2" + # sysctls: + # net.core.somaxconn: 32768 + # ulimits: + # nofile: + # soft: 1000000 + # hard: 1000000 + restart: on-failure + + tikv2: + image: pingcap/tikv:latest + volumes: + - ./config/tikv.toml:/tikv.toml:ro + - ./data:/data + - ./logs:/logs + command: + - --addr=0.0.0.0:20160 + - --advertise-addr=tikv2:20160 + - --data-dir=/data/tikv2 + - --pd=pd0:2379,pd1:2379,pd2:2379 + - --config=/tikv.toml + - --log-file=/logs/tikv2.log + depends_on: + - "pd0" + - "pd1" + - "pd2" + # sysctls: + # net.core.somaxconn: 32768 + # ulimits: + # nofile: + # soft: 1000000 + # hard: 1000000 + restart: on-failure + + pump0: + image: pingcap/tidb-binlog:latest + volumes: + - ./config/pump.toml:/pump.toml:ro + - ./data:/data + - ./logs:/logs + command: + - /pump + - --addr=0.0.0.0:8250 + - --advertise-addr=pump0:8250 + - --data-dir=/data/pump0 + - --log-file=/logs/pump0.log + - --node-id=pump0 + - --pd-urls=http://pd0:2379,http://pd1:2379,http://pd2:2379 + - --config=/pump.toml + depends_on: + - "pd0" + - "pd1" + - "pd2" + restart: on-failure + + pump1: + image: pingcap/tidb-binlog:latest + volumes: + - ./config/pump.toml:/pump.toml:ro + - ./data:/data + - ./logs:/logs + command: + - /pump + - --addr=0.0.0.0:8250 + - --advertise-addr=pump1:8250 + - --data-dir=/data/pump1 + - --log-file=/logs/pump1.log + - --node-id=pump1 + - --pd-urls=http://pd0:2379,http://pd1:2379,http://pd2:2379 + - --config=/pump.toml + depends_on: + - "pd0" + - "pd1" + - "pd2" + restart: on-failure + + pump2: + image: pingcap/tidb-binlog:latest + volumes: + - ./config/pump.toml:/pump.toml:ro + - ./data:/data + - ./logs:/logs + command: + - /pump + - --addr=0.0.0.0:8250 + - --advertise-addr=pump2:8250 + - --data-dir=/data/pump2 + - --log-file=/logs/pump2.log + - --node-id=pump2 + - --pd-urls=http://pd0:2379,http://pd1:2379,http://pd2:2379 + - --config=/pump.toml + depends_on: + - "pd0" + - "pd1" + - "pd2" + restart: on-failure + + drainer: + image: pingcap/tidb-binlog:latest + volumes: + - ./config/drainer.toml:/drainer.toml:ro + - ./data:/data + - ./logs:/logs + command: + - /drainer + - --addr=0.0.0.0:8249 + - --data-dir=/data/data.drainer + - --log-file=/logs/drainer.log + - --pd-urls=http://pd0:2379,http://pd1:2379,http://pd2:2379 + - --config=/drainer.toml + - --initial-commit-ts=0 + - --dest-db-type=kafka + depends_on: + - "pd0" + - "pd1" + - "pd2" + - "kafka0" + - "kafka1" + - "kafka2" + restart: on-failure + + zoo0: + image: zookeeper:latest + ports: + - "2181:2181" + environment: + ZOO_MY_ID: 1 + ZOO_PORT: 2181 + ZOO_SERVERS: server.1=zoo0:2888:3888 server.2=zoo1:2888:3888 server.3=zoo2:2888:3888 + volumes: + - ./data/zoo0/data:/data + - ./data/zoo0/datalog:/datalog + restart: on-failure + + zoo1: + image: zookeeper:latest + ports: + - "2182:2182" + environment: + ZOO_MY_ID: 2 + ZOO_PORT: 2182 + ZOO_SERVERS: server.1=zoo0:2888:3888 server.2=zoo1:2888:3888 server.3=zoo2:2888:3888 + volumes: + - ./data/zoo1/data:/data + - ./data/zoo1/datalog:/datalog + restart: on-failure + + zoo2: + image: zookeeper:latest + ports: + - "2183:2183" + environment: + ZOO_MY_ID: 3 + ZOO_PORT: 2183 + ZOO_SERVERS: server.1=zoo0:2888:3888 server.2=zoo1:2888:3888 server.3=zoo2:2888:3888 + volumes: + - ./data/zoo2/data:/data + - ./data/zoo2/datalog:/datalog + restart: on-failure + + kafka0: + image: wurstmeister/kafka:2.12-2.1.1 + ports: + - "9092:9092" + environment: + KAFKA_BROKER_ID: 1 + KAFKA_LOG_DIRS: /data/kafka-logs + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka0:9092 + KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092 + KAFKA_ZOOKEEPER_CONNECT: zoo0:2181,zoo1:2182,zoo2:2183 + volumes: + - ./data/kafka-logs/kafka0:/data/kafka-logs + - ./logs/kafka0:/opt/kafka/logs + - /var/run/docker.sock:/var/run/docker.sock + depends_on: + - "zoo0" + - "zoo1" + - "zoo2" + restart: on-failure + kafka1: + image: wurstmeister/kafka:2.12-2.1.1 + ports: + - "9093:9093" + environment: + KAFKA_BROKER_ID: 2 + KAFKA_LOG_DIRS: /data/kafka-logs + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka1:9093 + KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9093 + KAFKA_ZOOKEEPER_CONNECT: zoo0:2181,zoo1:2182,zoo2:2183 + volumes: + - ./data/kafka-logs/kafka1:/data/kafka-logs + - ./logs/kafka1:/opt/kafka/logs + - /var/run/docker.sock:/var/run/docker.sock + depends_on: + - "zoo0" + - "zoo1" + - "zoo2" + restart: on-failure + kafka2: + image: wurstmeister/kafka:2.12-2.1.1 + ports: + - "9094:9094" + environment: + KAFKA_BROKER_ID: 3 + KAFKA_LOG_DIRS: /data/kafka-logs + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka2:9094 + KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9094 + KAFKA_ZOOKEEPER_CONNECT: zoo0:2181,zoo1:2182,zoo2:2183 + volumes: + - ./data/kafka-logs/kafka2:/data/kafka-logs + - ./logs/kafka2:/opt/kafka/logs + - /var/run/docker.sock:/var/run/docker.sock + depends_on: + - "zoo0" + - "zoo1" + - "zoo2" + restart: on-failure + + tidb: + image: pingcap/tidb:latest + ports: + - "4000:4000" + - "10080:10080" + volumes: + - ./config/tidb.toml:/tidb.toml:ro + - ./logs:/logs + command: + - --store=tikv + - --path=pd0:2379,pd1:2379,pd2:2379 + - --config=/tidb.toml + - --log-file=/logs/tidb.log + - --advertise-address=tidb + - --enable-binlog=true + depends_on: + - "tikv0" + - "tikv1" + - "tikv2" + - "pump0" + - "pump1" + - "pump2" + # sysctls: + # net.core.somaxconn: 32768 + # ulimits: + # nofile: + # soft: 1000000 + # hard: 1000000 + restart: on-failure + + tispark-master: + image: pingcap/tispark:latest + command: + - /opt/spark/sbin/start-master.sh + volumes: + - ./config/spark-defaults.conf:/opt/spark/conf/spark-defaults.conf:ro + environment: + SPARK_MASTER_PORT: 7077 + SPARK_MASTER_WEBUI_PORT: 8080 + ports: + - "7077:7077" + - "8080:8080" + depends_on: + - "tikv0" + - "tikv1" + - "tikv2" + restart: on-failure + tispark-slave0: + image: pingcap/tispark:latest + command: + - /opt/spark/sbin/start-slave.sh + - spark://tispark-master:7077 + volumes: + - ./config/spark-defaults.conf:/opt/spark/conf/spark-defaults.conf:ro + environment: + SPARK_WORKER_WEBUI_PORT: 38081 + ports: + - "38081:38081" + depends_on: + - tispark-master + restart: on-failure + + tidb-vision: + image: pingcap/tidb-vision:latest + environment: + PD_ENDPOINT: pd0:2379 + ports: + - "8010:8010" + restart: on-failure + pushgateway: + image: prom/pushgateway:v0.3.1 + command: + - --log.level=error + restart: on-failure + + prometheus: + user: root + image: prom/prometheus:v2.2.1 + command: + - --log.level=error + - --storage.tsdb.path=/data/prometheus + - --config.file=/etc/prometheus/prometheus.yml + ports: + - "9090:9090" + volumes: + - ./config/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - ./config/pd.rules.yml:/etc/prometheus/pd.rules.yml:ro + - ./config/tikv.rules.yml:/etc/prometheus/tikv.rules.yml:ro + - ./config/tidb.rules.yml:/etc/prometheus/tidb.rules.yml:ro + - ./data:/data + restart: on-failure + grafana: + image: grafana/grafana:5.3.0 + user: "0" + environment: + GF_LOG_LEVEL: error + GF_PATHS_PROVISIONING: /etc/grafana/provisioning + GF_PATHS_CONFIG: /etc/grafana/grafana.ini + ports: + - "3000:3000" + volumes: + - ./config/grafana:/etc/grafana + - ./config/dashboards:/tmp/dashboards + - ./data/grafana:/var/lib/grafana + restart: on-failure diff --git a/docker/tidb/docker-compose-test.yml b/docker/tidb/docker-compose-test.yml new file mode 100755 index 0000000..2801b88 --- /dev/null +++ b/docker/tidb/docker-compose-test.yml @@ -0,0 +1,13 @@ +version: '2.1' + +services: + tispark-tests: + image: pingcap/tispark:latest + volumes: + - ./config/spark-defaults.conf:/opt/spark/conf/spark-defaults.conf:ro + - ./tispark-tests/tests:/opt/spark/tests:ro + +networks: + default: + external: + name: tidb-docker-compose_default diff --git a/docker/tidb/docker-compose-tiflash-nightly.yml b/docker/tidb/docker-compose-tiflash-nightly.yml new file mode 100755 index 0000000..72a468b --- /dev/null +++ b/docker/tidb/docker-compose-tiflash-nightly.yml @@ -0,0 +1,66 @@ +version: '2.1' + +services: + pd0: + image: pingcap/pd:nightly + ports: + - "2379" + volumes: + - ./config/pd-nightly-tiflash.toml:/pd.toml:ro + - ./data:/data + - ./logs:/logs + command: + - --name=pd0 + - --client-urls=http://0.0.0.0:2379 + - --peer-urls=http://0.0.0.0:2380 + - --advertise-client-urls=http://pd0:2379 + - --advertise-peer-urls=http://pd0:2380 + - --initial-cluster=pd0=http://pd0:2380 + - --data-dir=/data/pd + - --config=/pd.toml + - --log-file=/logs/pd.log + restart: on-failure + tikv: + image: pingcap/tikv:nightly + volumes: + - ./data:/data + - ./logs:/logs + command: + - --addr=0.0.0.0:20160 + - --advertise-addr=tikv:20160 + - --status-addr=tikv:20180 + - --data-dir=/data/tikv + - --pd=pd0:2379 + - --log-file=/logs/tikv.log + depends_on: + - "pd0" + restart: on-failure + tidb: + image: pingcap/tidb:nightly + ports: + - "4000:4000" + - "10080:10080" + volumes: + - ./logs:/logs + command: + - --status=10080 + - --advertise-address=tidb + - --store=tikv + - --path=pd0:2379 + - --log-file=/logs/tidb.log + depends_on: + - "tikv" + restart: on-failure + tiflash: + image: pingcap/tiflash:nightly + volumes: + - ./config/tiflash-nightly.toml:/tiflash.toml:ro + - ./config/tiflash-learner-nightly.toml:/tiflash-learner.toml:ro + - ./data:/data + - ./logs:/logs + command: + - --config=/tiflash.toml + depends_on: + - "tikv" + - "tidb" + restart: on-failure diff --git a/docker/tidb/docker-compose.yml b/docker/tidb/docker-compose.yml new file mode 100755 index 0000000..324962d --- /dev/null +++ b/docker/tidb/docker-compose.yml @@ -0,0 +1,210 @@ +version: '2.1' + +services: + pd0: + image: pingcap/pd:latest + ports: + - "2379" + volumes: + - ./config/pd.toml:/pd.toml:ro + - ./data:/data + - ./logs:/logs + command: + - --name=pd0 + - --client-urls=http://0.0.0.0:2379 + - --peer-urls=http://0.0.0.0:2380 + - --advertise-client-urls=http://pd0:2379 + - --advertise-peer-urls=http://pd0:2380 + - --initial-cluster=pd0=http://pd0:2380,pd1=http://pd1:2380,pd2=http://pd2:2380 + - --data-dir=/data/pd0 + - --config=/pd.toml + - --log-file=/logs/pd0.log + restart: on-failure + pd1: + image: pingcap/pd:latest + ports: + - "2379" + volumes: + - ./config/pd.toml:/pd.toml:ro + - ./data:/data + - ./logs:/logs + command: + - --name=pd1 + - --client-urls=http://0.0.0.0:2379 + - --peer-urls=http://0.0.0.0:2380 + - --advertise-client-urls=http://pd1:2379 + - --advertise-peer-urls=http://pd1:2380 + - --initial-cluster=pd0=http://pd0:2380,pd1=http://pd1:2380,pd2=http://pd2:2380 + - --data-dir=/data/pd1 + - --config=/pd.toml + - --log-file=/logs/pd1.log + restart: on-failure + pd2: + image: pingcap/pd:latest + ports: + - "2379" + volumes: + - ./config/pd.toml:/pd.toml:ro + - ./data:/data + - ./logs:/logs + command: + - --name=pd2 + - --client-urls=http://0.0.0.0:2379 + - --peer-urls=http://0.0.0.0:2380 + - --advertise-client-urls=http://pd2:2379 + - --advertise-peer-urls=http://pd2:2380 + - --initial-cluster=pd0=http://pd0:2380,pd1=http://pd1:2380,pd2=http://pd2:2380 + - --data-dir=/data/pd2 + - --config=/pd.toml + - --log-file=/logs/pd2.log + restart: on-failure + tikv0: + image: pingcap/tikv:latest + volumes: + - ./config/tikv.toml:/tikv.toml:ro + - ./data:/data + - ./logs:/logs + command: + - --addr=0.0.0.0:20160 + - --advertise-addr=tikv0:20160 + - --data-dir=/data/tikv0 + - --pd=pd0:2379,pd1:2379,pd2:2379 + - --config=/tikv.toml + - --log-file=/logs/tikv0.log + depends_on: + - "pd0" + - "pd1" + - "pd2" + restart: on-failure + tikv1: + image: pingcap/tikv:latest + volumes: + - ./config/tikv.toml:/tikv.toml:ro + - ./data:/data + - ./logs:/logs + command: + - --addr=0.0.0.0:20160 + - --advertise-addr=tikv1:20160 + - --data-dir=/data/tikv1 + - --pd=pd0:2379,pd1:2379,pd2:2379 + - --config=/tikv.toml + - --log-file=/logs/tikv1.log + depends_on: + - "pd0" + - "pd1" + - "pd2" + restart: on-failure + tikv2: + image: pingcap/tikv:latest + volumes: + - ./config/tikv.toml:/tikv.toml:ro + - ./data:/data + - ./logs:/logs + command: + - --addr=0.0.0.0:20160 + - --advertise-addr=tikv2:20160 + - --data-dir=/data/tikv2 + - --pd=pd0:2379,pd1:2379,pd2:2379 + - --config=/tikv.toml + - --log-file=/logs/tikv2.log + depends_on: + - "pd0" + - "pd1" + - "pd2" + restart: on-failure + + tidb: + image: pingcap/tidb:latest + ports: + - "4000:4000" + - "10080:10080" + volumes: + - ./config/tidb.toml:/tidb.toml:ro + - ./logs:/logs + command: + - --store=tikv + - --path=pd0:2379,pd1:2379,pd2:2379 + - --config=/tidb.toml + - --log-file=/logs/tidb.log + - --advertise-address=tidb + depends_on: + - "tikv0" + - "tikv1" + - "tikv2" + restart: on-failure + tispark-master: + image: pingcap/tispark:latest + command: + - /opt/spark/sbin/start-master.sh + volumes: + - ./config/spark-defaults.conf:/opt/spark/conf/spark-defaults.conf:ro + environment: + SPARK_MASTER_PORT: 7077 + SPARK_MASTER_WEBUI_PORT: 8080 + ports: + - "7077:7077" + - "8080:8080" + depends_on: + - "tikv0" + - "tikv1" + - "tikv2" + restart: on-failure + tispark-slave0: + image: pingcap/tispark:latest + command: + - /opt/spark/sbin/start-slave.sh + - spark://tispark-master:7077 + volumes: + - ./config/spark-defaults.conf:/opt/spark/conf/spark-defaults.conf:ro + environment: + SPARK_WORKER_WEBUI_PORT: 38081 + ports: + - "38081:38081" + depends_on: + - tispark-master + restart: on-failure + + tidb-vision: + image: pingcap/tidb-vision:latest + environment: + PD_ENDPOINT: pd0:2379 + ports: + - "8010:8010" + restart: on-failure + + # monitors + pushgateway: + image: prom/pushgateway:v0.3.1 + command: + - --log.level=error + restart: on-failure + prometheus: + user: root + image: prom/prometheus:v2.2.1 + command: + - --log.level=error + - --storage.tsdb.path=/data/prometheus + - --config.file=/etc/prometheus/prometheus.yml + ports: + - "9090:9090" + volumes: + - ./config/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - ./config/pd.rules.yml:/etc/prometheus/pd.rules.yml:ro + - ./config/tikv.rules.yml:/etc/prometheus/tikv.rules.yml:ro + - ./config/tidb.rules.yml:/etc/prometheus/tidb.rules.yml:ro + - ./data:/data + restart: on-failure + grafana: + image: grafana/grafana:6.0.1 + user: "0" + environment: + GF_LOG_LEVEL: error + GF_PATHS_PROVISIONING: /etc/grafana/provisioning + GF_PATHS_CONFIG: /etc/grafana/grafana.ini + volumes: + - ./config/grafana:/etc/grafana + - ./config/dashboards:/tmp/dashboards + - ./data/grafana:/var/lib/grafana + ports: + - "3000:3000" + restart: on-failure diff --git a/docker/tidb/docker-swarm.yml b/docker/tidb/docker-swarm.yml new file mode 100755 index 0000000..3635ccf --- /dev/null +++ b/docker/tidb/docker-swarm.yml @@ -0,0 +1,171 @@ +version: '3.3' + +networks: + default: + driver: overlay + attachable: true + +services: + pd0: + image: pingcap/pd:latest + ports: + - "2379" + volumes: + - ./config/pd.toml:/pd.toml:ro + - ./data:/data + - ./logs:/logs + command: + - --name=pd0 + - --client-urls=http://0.0.0.0:2379 + - --peer-urls=http://0.0.0.0:2380 + - --advertise-client-urls=http://pd0:2379 + - --advertise-peer-urls=http://pd0:2380 + - --initial-cluster=pd0=http://pd0:2380,pd1=http://pd1:2380,pd2=http://pd2:2380 + - --data-dir=/data/pd0 + - --config=/pd.toml + - --log-file=/logs/pd0.log + pd1: + image: pingcap/pd:latest + ports: + - "2379" + volumes: + - ./config/pd.toml:/pd.toml:ro + - ./data:/data + - ./logs:/logs + command: + - --name=pd1 + - --client-urls=http://0.0.0.0:2379 + - --peer-urls=http://0.0.0.0:2380 + - --advertise-client-urls=http://pd1:2379 + - --advertise-peer-urls=http://pd1:2380 + - --initial-cluster=pd0=http://pd0:2380,pd1=http://pd1:2380,pd2=http://pd2:2380 + - --data-dir=/data/pd1 + - --config=/pd.toml + - --log-file=/logs/pd1.log + pd2: + image: pingcap/pd:latest + ports: + - "2379" + volumes: + - ./config/pd.toml:/pd.toml:ro + - ./data:/data + - ./logs:/logs + command: + - --name=pd2 + - --client-urls=http://0.0.0.0:2379 + - --peer-urls=http://0.0.0.0:2380 + - --advertise-client-urls=http://pd2:2379 + - --advertise-peer-urls=http://pd2:2380 + - --initial-cluster=pd0=http://pd0:2380,pd1=http://pd1:2380,pd2=http://pd2:2380 + - --data-dir=/data/pd2 + - --config=/pd.toml + - --log-file=/logs/pd2.log + tikv: + image: pingcap/tikv:latest + ports: + - target: 20160 + published: 20160 + environment: + - TASK_SLOT={{.Task.Slot}} + volumes: + - ./config/tikv.toml:/tikv.toml:ro + - ./data:/data + - ./logs:/logs + entrypoint: [ "/bin/sh", "-c", "/tikv-server --advertise-addr=$$HOSTNAME:20160 --addr=0.0.0.0:20160 --data-dir=/data/tikv$$TASK_SLOT --pd=pd0:2379,pd1:2379,pd2:2379 --config=/tikv.toml --log-file=/logs/tikv$$TASK_SLOT.log --log-level=info" ] + depends_on: + - "pd0" + - "pd1" + - "pd2" + deploy: + replicas: 3 + restart_policy: + condition: on-failure + + tidb: + image: pingcap/tidb:latest + ports: + - target: 4000 + published: 4000 + - target: 10080 + published: 10080 + environment: + - TASK_SLOT={{.Task.Slot}} + volumes: + - ./config/tidb.toml:/tidb.toml:ro + - ./logs:/logs + entrypoint: [ "/bin/sh", "-c", "/tidb-server --advertise-address=$$HOSTNAME --store=tikv --path=pd0:2379,pd1:2379,pd2:2379 --config=/tidb.toml --log-file=/logs/tidb$$TASK_SLOT.log -L info" ] + depends_on: + - "tikv" + deploy: + replicas: 1 + + tispark-master: + image: pingcap/tispark:latest + command: + - /opt/spark/sbin/start-master.sh + volumes: + - ./config/spark-defaults.conf:/opt/spark/conf/spark-defaults.conf:ro + environment: + SPARK_MASTER_PORT: 7077 + SPARK_MASTER_WEBUI_PORT: 8080 + ports: + - "7077:7077" + - "8080:8080" + depends_on: + - "tikv" + deploy: + replicas: 1 + tispark-slave: + image: pingcap/tispark:latest + command: + - /opt/spark/sbin/start-slave.sh + - spark://tispark-master:7077 + volumes: + - ./config/spark-defaults.conf:/opt/spark/conf/spark-defaults.conf:ro + environment: + SPARK_WORKER_WEBUI_PORT: 38081 + ports: + - "38081:38081" + depends_on: + - tispark-master + deploy: + replicas: 1 + + tidb-vision: + image: pingcap/tidb-vision:latest + environment: + PD_ENDPOINT: pd0:2379 + ports: + - "8010:8010" + + # monitors + pushgateway: + image: prom/pushgateway:v0.3.1 + command: + - --log.level=error + prometheus: + user: root + image: prom/prometheus:v2.2.1 + command: + - --log.level=error + - --storage.tsdb.path=/data/prometheus + - --config.file=/etc/prometheus/prometheus.yml + ports: + - "9090:9090" + volumes: + - ./config/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - ./config/pd.rules.yml:/etc/prometheus/pd.rules.yml:ro + - ./config/tikv.rules.yml:/etc/prometheus/tikv.rules.yml:ro + - ./config/tidb.rules.yml:/etc/prometheus/tidb.rules.yml:ro + - ./data:/data + grafana: + image: grafana/grafana:6.0.1 + environment: + GF_LOG_LEVEL: error + GF_PATHS_PROVISIONING: /etc/grafana/provisioning + GF_PATHS_CONFIG: /etc/grafana/grafana.ini + volumes: + - ./config/grafana:/etc/grafana + - ./config/dashboards:/var/lib/grafana/dashboards + ports: + - "3000:3000" diff --git a/docker/tidb/docker/debug/Dockerfile b/docker/tidb/docker/debug/Dockerfile new file mode 100755 index 0000000..97b5fb2 --- /dev/null +++ b/docker/tidb/docker/debug/Dockerfile @@ -0,0 +1,50 @@ +FROM centos:7 + +RUN yum update -y && yum install -y \ + curl \ + file \ + gdb \ + git \ + iotop \ + linux-perf \ + mysql \ + net-tools \ + perf \ + perl \ + procps-ng \ + psmisc \ + strace \ + sysstat \ + tree \ + tcpdump \ + unzip \ + vim \ + wget \ + which \ + && yum clean all \ + && rm -rf /var/cache/yum/* + +RUN wget -q http://download.pingcap.org/tidb-latest-linux-amd64.tar.gz \ + && tar xzf tidb-latest-linux-amd64.tar.gz \ + && mv tidb-latest-linux-amd64/bin/* /usr/local/bin/ \ + && rm -rf tidb-latest-linux-amd64.tar.gz tidb-latest-linux-amd64 + +RUN wget https://github.com/brendangregg/FlameGraph/archive/master.zip \ + && unzip master.zip \ + && mv FlameGraph-master /opt/FlameGraph \ + && rm master.zip +ADD run_flamegraph.sh /run_flamegraph.sh + +# used for go pprof +ENV GOLANG_VERSION 1.10 +ENV GOLANG_DOWNLOAD_URL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz +ENV GOLANG_DOWNLOAD_SHA256 b5a64335f1490277b585832d1f6c7f8c6c11206cba5cd3f771dcb87b98ad1a33 +RUN curl -fsSL "$GOLANG_DOWNLOAD_URL" -o golang.tar.gz \ + && echo "$GOLANG_DOWNLOAD_SHA256 golang.tar.gz" | sha256sum -c - \ + && tar -C /usr/local -xzf golang.tar.gz \ + && rm golang.tar.gz +ENV GOPATH /go +ENV GOROOT /usr/local/go +ENV PATH $GOPATH/bin:$GOROOT/bin:$PATH + +ENTRYPOINT ["/bin/bash"] diff --git a/docker/tidb/docker/debug/run_flamegraph.sh b/docker/tidb/docker/debug/run_flamegraph.sh new file mode 100755 index 0000000..e82ac10 --- /dev/null +++ b/docker/tidb/docker/debug/run_flamegraph.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -e + +perf record -F 99 -p $1 -g -- sleep 60 +perf script > out.perf +/opt/FlameGraph/stackcollapse-perf.pl out.perf > out.folded +/opt/FlameGraph/flamegraph.pl out.folded > kernel.svg +curl --upload-file ./kernel.svg https://transfer.sh/kernel.svg diff --git a/docker/tidb/pd/Dockerfile b/docker/tidb/pd/Dockerfile new file mode 100755 index 0000000..96c2f23 --- /dev/null +++ b/docker/tidb/pd/Dockerfile @@ -0,0 +1,9 @@ +FROM alpine:3.5 + +ADD bin/pd-server /pd-server + +WORKDIR / + +EXPOSE 2379 2380 + +ENTRYPOINT ["/pd-server"] diff --git a/docker/tidb/tidb-binlog/Dockerfile b/docker/tidb/tidb-binlog/Dockerfile new file mode 100755 index 0000000..6d70a5c --- /dev/null +++ b/docker/tidb/tidb-binlog/Dockerfile @@ -0,0 +1,11 @@ +from alpine:3.5 + + ADD bin/pump /pump + + ADD bin/drainer /drainer + + RUN chmod +x /pump /drainer + + WORKDIR / + + EXPOSE 8249 8250 diff --git a/docker/tidb/tidb-vision/Dockerfile b/docker/tidb/tidb-vision/Dockerfile new file mode 100755 index 0000000..9a5e898 --- /dev/null +++ b/docker/tidb/tidb-vision/Dockerfile @@ -0,0 +1,13 @@ +FROM node:8 + +ADD tidb-vision /home/node/tidb-vision + +WORKDIR /home/node/tidb-vision + +RUN npm install + +ENV PD_ENDPOINT=localhost:9000 + +EXPOSE 8010 + +CMD ["npm", "start"] diff --git a/docker/tidb/tidb/Dockerfile b/docker/tidb/tidb/Dockerfile new file mode 100755 index 0000000..410af77 --- /dev/null +++ b/docker/tidb/tidb/Dockerfile @@ -0,0 +1,11 @@ +from alpine:3.5 + +ADD bin/tidb-server /tidb-server + +RUN chmod +x /tidb-server + +WORKDIR / + +EXPOSE 4000 10080 + +ENTRYPOINT ["/tidb-server"] diff --git a/docker/tidb/tikv/Dockerfile b/docker/tidb/tikv/Dockerfile new file mode 100755 index 0000000..ebb61b5 --- /dev/null +++ b/docker/tidb/tikv/Dockerfile @@ -0,0 +1,11 @@ +FROM pingcap/alpine-glibc + +ADD bin/tikv-server /tikv-server + +RUN chmod +x /tikv-server + +WORKDIR / + +EXPOSE 20160 + +ENTRYPOINT ["/tikv-server"] diff --git a/docker/tidb/tispark/Dockerfile b/docker/tidb/tispark/Dockerfile new file mode 100755 index 0000000..52f6d77 --- /dev/null +++ b/docker/tidb/tispark/Dockerfile @@ -0,0 +1,40 @@ +FROM anapsix/alpine-java:8 + +ENV SPARK_VERSION=2.4.3 \ + HADOOP_VERSION=2.7 \ + TISPARK_PYTHON_VERSION=2.0 \ + SPARK_HOME=/opt/spark \ + SPARK_NO_DAEMONIZE=true \ + SPARK_MASTER_PORT=7077 \ + SPARK_MASTER_HOST=0.0.0.0 \ + SPARK_MASTER_WEBUI_PORT=8080 + +ADD tispark-tests /opt/tispark-tests + +# base image only contains busybox version nohup and ps +# spark scripts needs nohup in coreutils and ps in procps +# and we can use mysql-client to test tidb connection +RUN apk --no-cache add \ + coreutils \ + mysql-client \ + procps \ + python \ + py-pip \ + R + +RUN wget -q https://download.pingcap.org/spark-${SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz \ + && tar zxf spark-${SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz -C /opt/ \ + && ln -s /opt/spark-${SPARK_VERSION}-bin-hadoop${HADOOP_VERSION} ${SPARK_HOME} \ + && wget -q http://download.pingcap.org/tispark-assembly-latest-linux-amd64.tar.gz \ + && tar zxf ./tispark-assembly-latest-linux-amd64.tar.gz -C /opt/ \ + && cp /opt/assembly/target/tispark-assembly-*.jar ${SPARK_HOME}/jars \ + && wget -q http://download.pingcap.org/tispark-sample-data.tar.gz \ + && tar zxf tispark-sample-data.tar.gz -C ${SPARK_HOME}/data/ \ + && rm -rf /opt/assembly/ spark-${SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz tispark-latest-linux-amd64.tar.gz tispark-sample-data.tar.gz + +ADD spark-${SPARK_VERSION}/session.py ${SPARK_HOME}/python/pyspark/sql/ +ADD conf/log4j.properties /opt/spark/conf/log4j.properties + +ENV PYTHONPATH=${SPARK_HOME}/python/lib/py4j-0.10.4-src.zip:${SPARK_HOME}/python:$PYTHONPATH + +WORKDIR ${SPARK_HOME} diff --git a/docker/tidb/tispark/conf/log4j.properties b/docker/tidb/tispark/conf/log4j.properties new file mode 100755 index 0000000..d9d1936 --- /dev/null +++ b/docker/tidb/tispark/conf/log4j.properties @@ -0,0 +1,43 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Set everything to be logged to the console +log4j.rootCategory=INFO, console +log4j.appender.console=org.apache.log4j.ConsoleAppender +log4j.appender.console.target=System.err +log4j.appender.console.layout=org.apache.log4j.PatternLayout +log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n + +# Set the default spark-shell log level to WARN. When running the spark-shell, the +# log level for this class is used to overwrite the root logger's log level, so that +# the user can have different defaults for the shell and regular Spark apps. +log4j.logger.org.apache.spark.repl.Main=WARN + +# Settings to quiet third party logs that are too verbose +log4j.logger.org.spark_project.jetty=WARN +log4j.logger.org.spark_project.jetty.util.component.AbstractLifeCycle=ERROR +log4j.logger.org.apache.spark.repl.SparkIMain$exprTyper=INFO +log4j.logger.org.apache.spark.repl.SparkILoop$SparkILoopInterpreter=INFO +log4j.logger.org.apache.parquet=ERROR +log4j.logger.parquet=ERROR + +# SPARK-9183: Settings to avoid annoying messages when looking up nonexistent UDFs in SparkSQL with Hive support +log4j.logger.org.apache.hadoop.hive.metastore.RetryingHMSHandler=FATAL +log4j.logger.org.apache.hadoop.hive.ql.exec.FunctionRegistry=ERROR + +# tispark disable "WARN ObjectStore:568 - Failed to get database" +log4j.logger.org.apache.hadoop.hive.metastore.ObjectStore=ERROR diff --git a/docker/tidb/tispark/spark-2.3.3/session.py b/docker/tidb/tispark/spark-2.3.3/session.py new file mode 100755 index 0000000..b29de16 --- /dev/null +++ b/docker/tidb/tispark/spark-2.3.3/session.py @@ -0,0 +1,811 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from __future__ import print_function +import sys +import warnings +from functools import reduce +from threading import RLock + +if sys.version >= '3': + basestring = unicode = str + xrange = range +else: + from itertools import izip as zip, imap as map + +from pyspark import since +from pyspark.rdd import RDD, ignore_unicode_prefix +from pyspark.sql.conf import RuntimeConfig +from pyspark.sql.dataframe import DataFrame +from pyspark.sql.readwriter import DataFrameReader +from pyspark.sql.streaming import DataStreamReader +from pyspark.sql.types import Row, DataType, StringType, StructType, TimestampType, \ + _make_type_verifier, _infer_schema, _has_nulltype, _merge_type, _create_converter, \ + _parse_datatype_string +from pyspark.sql.utils import install_exception_handler + +__all__ = ["SparkSession"] + + +def _monkey_patch_RDD(sparkSession): + def toDF(self, schema=None, sampleRatio=None): + """ + Converts current :class:`RDD` into a :class:`DataFrame` + + This is a shorthand for ``spark.createDataFrame(rdd, schema, sampleRatio)`` + + :param schema: a :class:`pyspark.sql.types.StructType` or list of names of columns + :param samplingRatio: the sample ratio of rows used for inferring + :return: a DataFrame + + >>> rdd.toDF().collect() + [Row(name=u'Alice', age=1)] + """ + return sparkSession.createDataFrame(self, schema, sampleRatio) + + RDD.toDF = toDF + + +class SparkSession(object): + """The entry point to programming Spark with the Dataset and DataFrame API. + + A SparkSession can be used create :class:`DataFrame`, register :class:`DataFrame` as + tables, execute SQL over tables, cache tables, and read parquet files. + To create a SparkSession, use the following builder pattern: + + >>> spark = SparkSession.builder \\ + ... .master("local") \\ + ... .appName("Word Count") \\ + ... .config("spark.some.config.option", "some-value") \\ + ... .getOrCreate() + + .. autoattribute:: builder + :annotation: + """ + + class Builder(object): + """Builder for :class:`SparkSession`. + """ + + _lock = RLock() + _options = {} + + @since(2.0) + def config(self, key=None, value=None, conf=None): + """Sets a config option. Options set using this method are automatically propagated to + both :class:`SparkConf` and :class:`SparkSession`'s own configuration. + + For an existing SparkConf, use `conf` parameter. + + >>> from pyspark.conf import SparkConf + >>> SparkSession.builder.config(conf=SparkConf()) + >> SparkSession.builder.config("spark.some.config.option", "some-value") + >> s1 = SparkSession.builder.config("k1", "v1").getOrCreate() + >>> s1.conf.get("k1") == s1.sparkContext.getConf().get("k1") == "v1" + True + + In case an existing SparkSession is returned, the config options specified + in this builder will be applied to the existing SparkSession. + + >>> s2 = SparkSession.builder.config("k2", "v2").getOrCreate() + >>> s1.conf.get("k1") == s2.conf.get("k1") + True + >>> s1.conf.get("k2") == s2.conf.get("k2") + True + """ + with self._lock: + from pyspark.context import SparkContext + from pyspark.conf import SparkConf + session = SparkSession._instantiatedSession + if session is None or session._sc._jsc is None: + sparkConf = SparkConf() + for key, value in self._options.items(): + sparkConf.set(key, value) + sc = SparkContext.getOrCreate(sparkConf) + # This SparkContext may be an existing one. + for key, value in self._options.items(): + # we need to propagate the confs + # before we create the SparkSession. Otherwise, confs like + # warehouse path and metastore url will not be set correctly ( + # these confs cannot be changed once the SparkSession is created). + sc._conf.set(key, value) + session = SparkSession(sc) + for key, value in self._options.items(): + session._jsparkSession.sessionState().conf().setConfString(key, value) + for key, value in self._options.items(): + session.sparkContext._conf.set(key, value) + return session + + builder = Builder() + """A class attribute having a :class:`Builder` to construct :class:`SparkSession` instances""" + + _instantiatedSession = None + + @ignore_unicode_prefix + def __init__(self, sparkContext, jsparkSession=None): + """Creates a new SparkSession. + + >>> from datetime import datetime + >>> spark = SparkSession(sc) + >>> allTypes = sc.parallelize([Row(i=1, s="string", d=1.0, l=1, + ... b=True, list=[1, 2, 3], dict={"s": 0}, row=Row(a=1), + ... time=datetime(2014, 8, 1, 14, 1, 5))]) + >>> df = allTypes.toDF() + >>> df.createOrReplaceTempView("allTypes") + >>> spark.sql('select i+1, d+1, not b, list[1], dict["s"], time, row.a ' + ... 'from allTypes where b and i > 0').collect() + [Row((i + CAST(1 AS BIGINT))=2, (d + CAST(1 AS DOUBLE))=2.0, (NOT b)=False, list[1]=2, \ + dict[s]=0, time=datetime.datetime(2014, 8, 1, 14, 1, 5), a=1)] + >>> df.rdd.map(lambda x: (x.i, x.s, x.d, x.l, x.b, x.time, x.row.a, x.list)).collect() + [(1, u'string', 1.0, 1, True, datetime.datetime(2014, 8, 1, 14, 1, 5), 1, [1, 2, 3])] + """ + from pyspark.sql.context import SQLContext + self._sc = sparkContext + self._jsc = self._sc._jsc + self._jvm = self._sc._jvm + if jsparkSession is None: + jsparkSession = self._jvm.SparkSession.builder().getOrCreate() + self._jsparkSession = jsparkSession + self._jwrapped = self._jsparkSession.sqlContext() + self._wrapped = SQLContext(self._sc, self, self._jwrapped) + _monkey_patch_RDD(self) + install_exception_handler() + # If we had an instantiated SparkSession attached with a SparkContext + # which is stopped now, we need to renew the instantiated SparkSession. + # Otherwise, we will use invalid SparkSession when we call Builder.getOrCreate. + if SparkSession._instantiatedSession is None \ + or SparkSession._instantiatedSession._sc._jsc is None: + SparkSession._instantiatedSession = self + + def _repr_html_(self): + return """ +
+

SparkSession - {catalogImplementation}

+ {sc_HTML} +
+ """.format( + catalogImplementation=self.conf.get("spark.sql.catalogImplementation"), + sc_HTML=self.sparkContext._repr_html_() + ) + + @since(2.0) + def newSession(self): + """ + Returns a new SparkSession as new session, that has separate SQLConf, + registered temporary views and UDFs, but shared SparkContext and + table cache. + """ + return self.__class__(self._sc, self._jsparkSession.newSession()) + + @property + @since(2.0) + def sparkContext(self): + """Returns the underlying :class:`SparkContext`.""" + return self._sc + + @property + @since(2.0) + def version(self): + """The version of Spark on which this application is running.""" + return self._jsparkSession.version() + + @property + @since(2.0) + def conf(self): + """Runtime configuration interface for Spark. + + This is the interface through which the user can get and set all Spark and Hadoop + configurations that are relevant to Spark SQL. When getting the value of a config, + this defaults to the value set in the underlying :class:`SparkContext`, if any. + """ + if not hasattr(self, "_conf"): + self._conf = RuntimeConfig(self._jsparkSession.conf()) + return self._conf + + @property + @since(2.0) + def catalog(self): + """Interface through which the user may create, drop, alter or query underlying + databases, tables, functions etc. + + :return: :class:`Catalog` + """ + from pyspark.sql.catalog import Catalog + if not hasattr(self, "_catalog"): + self._catalog = Catalog(self) + return self._catalog + + @property + @since(2.0) + def udf(self): + """Returns a :class:`UDFRegistration` for UDF registration. + + :return: :class:`UDFRegistration` + """ + from pyspark.sql.udf import UDFRegistration + return UDFRegistration(self) + + @since(2.0) + def range(self, start, end=None, step=1, numPartitions=None): + """ + Create a :class:`DataFrame` with single :class:`pyspark.sql.types.LongType` column named + ``id``, containing elements in a range from ``start`` to ``end`` (exclusive) with + step value ``step``. + + :param start: the start value + :param end: the end value (exclusive) + :param step: the incremental step (default: 1) + :param numPartitions: the number of partitions of the DataFrame + :return: :class:`DataFrame` + + >>> spark.range(1, 7, 2).collect() + [Row(id=1), Row(id=3), Row(id=5)] + + If only one argument is specified, it will be used as the end value. + + >>> spark.range(3).collect() + [Row(id=0), Row(id=1), Row(id=2)] + """ + if numPartitions is None: + numPartitions = self._sc.defaultParallelism + + if end is None: + jdf = self._jsparkSession.range(0, int(start), int(step), int(numPartitions)) + else: + jdf = self._jsparkSession.range(int(start), int(end), int(step), int(numPartitions)) + + return DataFrame(jdf, self._wrapped) + + def _inferSchemaFromList(self, data, names=None): + """ + Infer schema from list of Row or tuple. + + :param data: list of Row or tuple + :param names: list of column names + :return: :class:`pyspark.sql.types.StructType` + """ + if not data: + raise ValueError("can not infer schema from empty dataset") + first = data[0] + if type(first) is dict: + warnings.warn("inferring schema from dict is deprecated," + "please use pyspark.sql.Row instead") + schema = reduce(_merge_type, (_infer_schema(row, names) for row in data)) + if _has_nulltype(schema): + raise ValueError("Some of types cannot be determined after inferring") + return schema + + def _inferSchema(self, rdd, samplingRatio=None, names=None): + """ + Infer schema from an RDD of Row or tuple. + + :param rdd: an RDD of Row or tuple + :param samplingRatio: sampling ratio, or no sampling (default) + :return: :class:`pyspark.sql.types.StructType` + """ + first = rdd.first() + if not first: + raise ValueError("The first row in RDD is empty, " + "can not infer schema") + if type(first) is dict: + warnings.warn("Using RDD of dict to inferSchema is deprecated. " + "Use pyspark.sql.Row instead") + + if samplingRatio is None: + schema = _infer_schema(first, names=names) + if _has_nulltype(schema): + for row in rdd.take(100)[1:]: + schema = _merge_type(schema, _infer_schema(row, names=names)) + if not _has_nulltype(schema): + break + else: + raise ValueError("Some of types cannot be determined by the " + "first 100 rows, please try again with sampling") + else: + if samplingRatio < 0.99: + rdd = rdd.sample(False, float(samplingRatio)) + schema = rdd.map(lambda row: _infer_schema(row, names)).reduce(_merge_type) + return schema + + def _createFromRDD(self, rdd, schema, samplingRatio): + """ + Create an RDD for DataFrame from an existing RDD, returns the RDD and schema. + """ + if schema is None or isinstance(schema, (list, tuple)): + struct = self._inferSchema(rdd, samplingRatio, names=schema) + converter = _create_converter(struct) + rdd = rdd.map(converter) + if isinstance(schema, (list, tuple)): + for i, name in enumerate(schema): + struct.fields[i].name = name + struct.names[i] = name + schema = struct + + elif not isinstance(schema, StructType): + raise TypeError("schema should be StructType or list or None, but got: %s" % schema) + + # convert python objects to sql data + rdd = rdd.map(schema.toInternal) + return rdd, schema + + def _createFromLocal(self, data, schema): + """ + Create an RDD for DataFrame from a list or pandas.DataFrame, returns + the RDD and schema. + """ + # make sure data could consumed multiple times + if not isinstance(data, list): + data = list(data) + + if schema is None or isinstance(schema, (list, tuple)): + struct = self._inferSchemaFromList(data, names=schema) + converter = _create_converter(struct) + data = map(converter, data) + if isinstance(schema, (list, tuple)): + for i, name in enumerate(schema): + struct.fields[i].name = name + struct.names[i] = name + schema = struct + + elif not isinstance(schema, StructType): + raise TypeError("schema should be StructType or list or None, but got: %s" % schema) + + # convert python objects to sql data + data = [schema.toInternal(row) for row in data] + return self._sc.parallelize(data), schema + + def _get_numpy_record_dtype(self, rec): + """ + Used when converting a pandas.DataFrame to Spark using to_records(), this will correct + the dtypes of fields in a record so they can be properly loaded into Spark. + :param rec: a numpy record to check field dtypes + :return corrected dtype for a numpy.record or None if no correction needed + """ + import numpy as np + cur_dtypes = rec.dtype + col_names = cur_dtypes.names + record_type_list = [] + has_rec_fix = False + for i in xrange(len(cur_dtypes)): + curr_type = cur_dtypes[i] + # If type is a datetime64 timestamp, convert to microseconds + # NOTE: if dtype is datetime[ns] then np.record.tolist() will output values as longs, + # conversion from [us] or lower will lead to py datetime objects, see SPARK-22417 + if curr_type == np.dtype('datetime64[ns]'): + curr_type = 'datetime64[us]' + has_rec_fix = True + record_type_list.append((str(col_names[i]), curr_type)) + return np.dtype(record_type_list) if has_rec_fix else None + + def _convert_from_pandas(self, pdf, schema, timezone): + """ + Convert a pandas.DataFrame to list of records that can be used to make a DataFrame + :return list of records + """ + if timezone is not None: + from pyspark.sql.types import _check_series_convert_timestamps_tz_local + copied = False + if isinstance(schema, StructType): + for field in schema: + # TODO: handle nested timestamps, such as ArrayType(TimestampType())? + if isinstance(field.dataType, TimestampType): + s = _check_series_convert_timestamps_tz_local(pdf[field.name], timezone) + if s is not pdf[field.name]: + if not copied: + # Copy once if the series is modified to prevent the original + # Pandas DataFrame from being updated + pdf = pdf.copy() + copied = True + pdf[field.name] = s + else: + for column, series in pdf.iteritems(): + s = _check_series_convert_timestamps_tz_local(series, timezone) + if s is not series: + if not copied: + # Copy once if the series is modified to prevent the original + # Pandas DataFrame from being updated + pdf = pdf.copy() + copied = True + pdf[column] = s + + # Convert pandas.DataFrame to list of numpy records + np_records = pdf.to_records(index=False) + + # Check if any columns need to be fixed for Spark to infer properly + if len(np_records) > 0: + record_dtype = self._get_numpy_record_dtype(np_records[0]) + if record_dtype is not None: + return [r.astype(record_dtype).tolist() for r in np_records] + + # Convert list of numpy records to python lists + return [r.tolist() for r in np_records] + + def _create_from_pandas_with_arrow(self, pdf, schema, timezone): + """ + Create a DataFrame from a given pandas.DataFrame by slicing it into partitions, converting + to Arrow data, then sending to the JVM to parallelize. If a schema is passed in, the + data types will be used to coerce the data in Pandas to Arrow conversion. + """ + from pyspark.serializers import ArrowSerializer, _create_batch + from pyspark.sql.types import from_arrow_schema, to_arrow_type, TimestampType + from pyspark.sql.utils import require_minimum_pandas_version, \ + require_minimum_pyarrow_version + + require_minimum_pandas_version() + require_minimum_pyarrow_version() + + from pandas.api.types import is_datetime64_dtype, is_datetime64tz_dtype + + # Determine arrow types to coerce data when creating batches + if isinstance(schema, StructType): + arrow_types = [to_arrow_type(f.dataType) for f in schema.fields] + elif isinstance(schema, DataType): + raise ValueError("Single data type %s is not supported with Arrow" % str(schema)) + else: + # Any timestamps must be coerced to be compatible with Spark + arrow_types = [to_arrow_type(TimestampType()) + if is_datetime64_dtype(t) or is_datetime64tz_dtype(t) else None + for t in pdf.dtypes] + + # Slice the DataFrame to be batched + step = -(-len(pdf) // self.sparkContext.defaultParallelism) # round int up + pdf_slices = (pdf[start:start + step] for start in xrange(0, len(pdf), step)) + + # Create Arrow record batches + batches = [_create_batch([(c, t) for (_, c), t in zip(pdf_slice.iteritems(), arrow_types)], + timezone) + for pdf_slice in pdf_slices] + + # Create the Spark schema from the first Arrow batch (always at least 1 batch after slicing) + if isinstance(schema, (list, tuple)): + struct = from_arrow_schema(batches[0].schema) + for i, name in enumerate(schema): + struct.fields[i].name = name + struct.names[i] = name + schema = struct + + # Create the Spark DataFrame directly from the Arrow data and schema + jrdd = self._sc._serialize_to_jvm(batches, len(batches), ArrowSerializer()) + jdf = self._jvm.PythonSQLUtils.arrowPayloadToDataFrame( + jrdd, schema.json(), self._wrapped._jsqlContext) + df = DataFrame(jdf, self._wrapped) + df._schema = schema + return df + + @since(2.0) + @ignore_unicode_prefix + def createDataFrame(self, data, schema=None, samplingRatio=None, verifySchema=True): + """ + Creates a :class:`DataFrame` from an :class:`RDD`, a list or a :class:`pandas.DataFrame`. + + When ``schema`` is a list of column names, the type of each column + will be inferred from ``data``. + + When ``schema`` is ``None``, it will try to infer the schema (column names and types) + from ``data``, which should be an RDD of :class:`Row`, + or :class:`namedtuple`, or :class:`dict`. + + When ``schema`` is :class:`pyspark.sql.types.DataType` or a datatype string, it must match + the real data, or an exception will be thrown at runtime. If the given schema is not + :class:`pyspark.sql.types.StructType`, it will be wrapped into a + :class:`pyspark.sql.types.StructType` as its only field, and the field name will be "value", + each record will also be wrapped into a tuple, which can be converted to row later. + + If schema inference is needed, ``samplingRatio`` is used to determined the ratio of + rows used for schema inference. The first row will be used if ``samplingRatio`` is ``None``. + + :param data: an RDD of any kind of SQL data representation(e.g. row, tuple, int, boolean, + etc.), or :class:`list`, or :class:`pandas.DataFrame`. + :param schema: a :class:`pyspark.sql.types.DataType` or a datatype string or a list of + column names, default is ``None``. The data type string format equals to + :class:`pyspark.sql.types.DataType.simpleString`, except that top level struct type can + omit the ``struct<>`` and atomic types use ``typeName()`` as their format, e.g. use + ``byte`` instead of ``tinyint`` for :class:`pyspark.sql.types.ByteType`. We can also use + ``int`` as a short name for ``IntegerType``. + :param samplingRatio: the sample ratio of rows used for inferring + :param verifySchema: verify data types of every row against schema. + :return: :class:`DataFrame` + + .. versionchanged:: 2.1 + Added verifySchema. + + >>> l = [('Alice', 1)] + >>> spark.createDataFrame(l).collect() + [Row(_1=u'Alice', _2=1)] + >>> spark.createDataFrame(l, ['name', 'age']).collect() + [Row(name=u'Alice', age=1)] + + >>> d = [{'name': 'Alice', 'age': 1}] + >>> spark.createDataFrame(d).collect() + [Row(age=1, name=u'Alice')] + + >>> rdd = sc.parallelize(l) + >>> spark.createDataFrame(rdd).collect() + [Row(_1=u'Alice', _2=1)] + >>> df = spark.createDataFrame(rdd, ['name', 'age']) + >>> df.collect() + [Row(name=u'Alice', age=1)] + + >>> from pyspark.sql import Row + >>> Person = Row('name', 'age') + >>> person = rdd.map(lambda r: Person(*r)) + >>> df2 = spark.createDataFrame(person) + >>> df2.collect() + [Row(name=u'Alice', age=1)] + + >>> from pyspark.sql.types import * + >>> schema = StructType([ + ... StructField("name", StringType(), True), + ... StructField("age", IntegerType(), True)]) + >>> df3 = spark.createDataFrame(rdd, schema) + >>> df3.collect() + [Row(name=u'Alice', age=1)] + + >>> spark.createDataFrame(df.toPandas()).collect() # doctest: +SKIP + [Row(name=u'Alice', age=1)] + >>> spark.createDataFrame(pandas.DataFrame([[1, 2]])).collect() # doctest: +SKIP + [Row(0=1, 1=2)] + + >>> spark.createDataFrame(rdd, "a: string, b: int").collect() + [Row(a=u'Alice', b=1)] + >>> rdd = rdd.map(lambda row: row[1]) + >>> spark.createDataFrame(rdd, "int").collect() + [Row(value=1)] + >>> spark.createDataFrame(rdd, "boolean").collect() # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + Py4JJavaError: ... + """ + if isinstance(data, DataFrame): + raise TypeError("data is already a DataFrame") + + if isinstance(schema, basestring): + schema = _parse_datatype_string(schema) + elif isinstance(schema, (list, tuple)): + # Must re-encode any unicode strings to be consistent with StructField names + schema = [x.encode('utf-8') if not isinstance(x, str) else x for x in schema] + + try: + import pandas + has_pandas = True + except Exception: + has_pandas = False + if has_pandas and isinstance(data, pandas.DataFrame): + from pyspark.sql.utils import require_minimum_pandas_version + require_minimum_pandas_version() + + if self.conf.get("spark.sql.execution.pandas.respectSessionTimeZone").lower() \ + == "true": + timezone = self.conf.get("spark.sql.session.timeZone") + else: + timezone = None + + # If no schema supplied by user then get the names of columns only + if schema is None: + schema = [str(x) if not isinstance(x, basestring) else + (x.encode('utf-8') if not isinstance(x, str) else x) + for x in data.columns] + + if self.conf.get("spark.sql.execution.arrow.enabled", "false").lower() == "true" \ + and len(data) > 0: + try: + return self._create_from_pandas_with_arrow(data, schema, timezone) + except Exception as e: + warnings.warn("Arrow will not be used in createDataFrame: %s" % str(e)) + # Fallback to create DataFrame without arrow if raise some exception + data = self._convert_from_pandas(data, schema, timezone) + + if isinstance(schema, StructType): + verify_func = _make_type_verifier(schema) if verifySchema else lambda _: True + + def prepare(obj): + verify_func(obj) + return obj + elif isinstance(schema, DataType): + dataType = schema + schema = StructType().add("value", schema) + + verify_func = _make_type_verifier( + dataType, name="field value") if verifySchema else lambda _: True + + def prepare(obj): + verify_func(obj) + return obj, + else: + prepare = lambda obj: obj + + if isinstance(data, RDD): + rdd, schema = self._createFromRDD(data.map(prepare), schema, samplingRatio) + else: + rdd, schema = self._createFromLocal(map(prepare, data), schema) + jrdd = self._jvm.SerDeUtil.toJavaArray(rdd._to_java_object_rdd()) + jdf = self._jsparkSession.applySchemaToPythonRDD(jrdd.rdd(), schema.json()) + df = DataFrame(jdf, self._wrapped) + df._schema = schema + return df + + @ignore_unicode_prefix + @since(2.0) + def sql(self, sqlQuery): + """Returns a :class:`DataFrame` representing the result of the given query. + + :return: :class:`DataFrame` + + >>> df.createOrReplaceTempView("table1") + >>> df2 = spark.sql("SELECT field1 AS f1, field2 as f2 from table1") + >>> df2.collect() + [Row(f1=1, f2=u'row1'), Row(f1=2, f2=u'row2'), Row(f1=3, f2=u'row3')] + """ + return DataFrame(self._jsparkSession.sql(sqlQuery), self._wrapped) + + @since(2.0) + def table(self, tableName): + """Returns the specified table as a :class:`DataFrame`. + + :return: :class:`DataFrame` + + >>> df.createOrReplaceTempView("table1") + >>> df2 = spark.table("table1") + >>> sorted(df.collect()) == sorted(df2.collect()) + True + """ + return DataFrame(self._jsparkSession.table(tableName), self._wrapped) + + @property + @since(2.0) + def read(self): + """ + Returns a :class:`DataFrameReader` that can be used to read data + in as a :class:`DataFrame`. + + :return: :class:`DataFrameReader` + """ + return DataFrameReader(self._wrapped) + + @property + @since(2.0) + def readStream(self): + """ + Returns a :class:`DataStreamReader` that can be used to read data streams + as a streaming :class:`DataFrame`. + + .. note:: Evolving. + + :return: :class:`DataStreamReader` + """ + return DataStreamReader(self._wrapped) + + @property + @since(2.0) + def streams(self): + """Returns a :class:`StreamingQueryManager` that allows managing all the + :class:`StreamingQuery` StreamingQueries active on `this` context. + + .. note:: Evolving. + + :return: :class:`StreamingQueryManager` + """ + from pyspark.sql.streaming import StreamingQueryManager + return StreamingQueryManager(self._jsparkSession.streams()) + + @since(2.0) + def stop(self): + """Stop the underlying :class:`SparkContext`. + """ + self._sc.stop() + SparkSession._instantiatedSession = None + + @since(2.0) + def __enter__(self): + """ + Enable 'with SparkSession.builder.(...).getOrCreate() as session: app' syntax. + """ + return self + + @since(2.0) + def __exit__(self, exc_type, exc_val, exc_tb): + """ + Enable 'with SparkSession.builder.(...).getOrCreate() as session: app' syntax. + + Specifically stop the SparkSession on exit of the with block. + """ + self.stop() + + +def _test(): + import os + import doctest + from pyspark.context import SparkContext + from pyspark.sql import Row + import pyspark.sql.session + + os.chdir(os.environ["SPARK_HOME"]) + + globs = pyspark.sql.session.__dict__.copy() + sc = SparkContext('local[4]', 'PythonTest') + globs['sc'] = sc + globs['spark'] = SparkSession(sc) + globs['rdd'] = rdd = sc.parallelize( + [Row(field1=1, field2="row1"), + Row(field1=2, field2="row2"), + Row(field1=3, field2="row3")]) + globs['df'] = rdd.toDF() + (failure_count, test_count) = doctest.testmod( + pyspark.sql.session, globs=globs, + optionflags=doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE) + globs['sc'].stop() + if failure_count: + exit(-1) + +if __name__ == "__main__": + _test() + diff --git a/docker/tidb/tispark/spark-2.4.3/session.py b/docker/tidb/tispark/spark-2.4.3/session.py new file mode 100755 index 0000000..d60a4c5 --- /dev/null +++ b/docker/tidb/tispark/spark-2.4.3/session.py @@ -0,0 +1,872 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from __future__ import print_function +import sys +import warnings +from functools import reduce +from threading import RLock + +if sys.version >= '3': + basestring = unicode = str + xrange = range +else: + from itertools import izip as zip, imap as map + +from pyspark import since +from pyspark.rdd import RDD, ignore_unicode_prefix +from pyspark.sql.conf import RuntimeConfig +from pyspark.sql.dataframe import DataFrame +from pyspark.sql.readwriter import DataFrameReader +from pyspark.sql.streaming import DataStreamReader +from pyspark.sql.types import Row, DataType, StringType, StructType, TimestampType, \ + _make_type_verifier, _infer_schema, _has_nulltype, _merge_type, _create_converter, \ + _parse_datatype_string +from pyspark.sql.utils import install_exception_handler + +__all__ = ["SparkSession"] + + +def _monkey_patch_RDD(sparkSession): + def toDF(self, schema=None, sampleRatio=None): + """ + Converts current :class:`RDD` into a :class:`DataFrame` + + This is a shorthand for ``spark.createDataFrame(rdd, schema, sampleRatio)`` + + :param schema: a :class:`pyspark.sql.types.StructType` or list of names of columns + :param samplingRatio: the sample ratio of rows used for inferring + :return: a DataFrame + + >>> rdd.toDF().collect() + [Row(name=u'Alice', age=1)] + """ + return sparkSession.createDataFrame(self, schema, sampleRatio) + + RDD.toDF = toDF + + +class SparkSession(object): + """The entry point to programming Spark with the Dataset and DataFrame API. + + A SparkSession can be used create :class:`DataFrame`, register :class:`DataFrame` as + tables, execute SQL over tables, cache tables, and read parquet files. + To create a SparkSession, use the following builder pattern: + + >>> spark = SparkSession.builder \\ + ... .master("local") \\ + ... .appName("Word Count") \\ + ... .config("spark.some.config.option", "some-value") \\ + ... .getOrCreate() + + .. autoattribute:: builder + :annotation: + """ + + class Builder(object): + """Builder for :class:`SparkSession`. + """ + + _lock = RLock() + _options = {} + + @since(2.0) + def config(self, key=None, value=None, conf=None): + """Sets a config option. Options set using this method are automatically propagated to + both :class:`SparkConf` and :class:`SparkSession`'s own configuration. + + For an existing SparkConf, use `conf` parameter. + + >>> from pyspark.conf import SparkConf + >>> SparkSession.builder.config(conf=SparkConf()) + >> SparkSession.builder.config("spark.some.config.option", "some-value") + >> s1 = SparkSession.builder.config("k1", "v1").getOrCreate() + >>> s1.conf.get("k1") == s1.sparkContext.getConf().get("k1") == "v1" + True + + In case an existing SparkSession is returned, the config options specified + in this builder will be applied to the existing SparkSession. + + >>> s2 = SparkSession.builder.config("k2", "v2").getOrCreate() + >>> s1.conf.get("k1") == s2.conf.get("k1") + True + >>> s1.conf.get("k2") == s2.conf.get("k2") + True + """ + with self._lock: + from pyspark.context import SparkContext + from pyspark.conf import SparkConf + session = SparkSession._instantiatedSession + if session is None or session._sc._jsc is None: + sparkConf = SparkConf() + for key, value in self._options.items(): + sparkConf.set(key, value) + sc = SparkContext.getOrCreate(sparkConf) + # This SparkContext may be an existing one. + for key, value in self._options.items(): + # we need to propagate the confs + # before we create the SparkSession. Otherwise, confs like + # warehouse path and metastore url will not be set correctly ( + # these confs cannot be changed once the SparkSession is created). + sc._conf.set(key, value) + session = SparkSession(sc) + for key, value in self._options.items(): + session._jsparkSession.sessionState().conf().setConfString(key, value) + for key, value in self._options.items(): + session.sparkContext._conf.set(key, value) + return session + + builder = Builder() + """A class attribute having a :class:`Builder` to construct :class:`SparkSession` instances""" + + _instantiatedSession = None + + @ignore_unicode_prefix + def __init__(self, sparkContext, jsparkSession=None): + """Creates a new SparkSession. + + >>> from datetime import datetime + >>> spark = SparkSession(sc) + >>> allTypes = sc.parallelize([Row(i=1, s="string", d=1.0, l=1, + ... b=True, list=[1, 2, 3], dict={"s": 0}, row=Row(a=1), + ... time=datetime(2014, 8, 1, 14, 1, 5))]) + >>> df = allTypes.toDF() + >>> df.createOrReplaceTempView("allTypes") + >>> spark.sql('select i+1, d+1, not b, list[1], dict["s"], time, row.a ' + ... 'from allTypes where b and i > 0').collect() + [Row((i + CAST(1 AS BIGINT))=2, (d + CAST(1 AS DOUBLE))=2.0, (NOT b)=False, list[1]=2, \ + dict[s]=0, time=datetime.datetime(2014, 8, 1, 14, 1, 5), a=1)] + >>> df.rdd.map(lambda x: (x.i, x.s, x.d, x.l, x.b, x.time, x.row.a, x.list)).collect() + [(1, u'string', 1.0, 1, True, datetime.datetime(2014, 8, 1, 14, 1, 5), 1, [1, 2, 3])] + """ + from pyspark.sql.context import SQLContext + self._sc = sparkContext + self._jsc = self._sc._jsc + self._jvm = self._sc._jvm + if jsparkSession is None: + if self._jvm.SparkSession.getDefaultSession().isDefined() \ + and not self._jvm.SparkSession.getDefaultSession().get() \ + .sparkContext().isStopped(): + jsparkSession = self._jvm.SparkSession.getDefaultSession().get() + else: + jsparkSession = self._jvm.SparkSession.builder().getOrCreate() +# jsparkSession = self._jvm.SparkSession(self._jsc.sc()) + self._jsparkSession = jsparkSession + self._jwrapped = self._jsparkSession.sqlContext() + self._wrapped = SQLContext(self._sc, self, self._jwrapped) + _monkey_patch_RDD(self) + install_exception_handler() + # If we had an instantiated SparkSession attached with a SparkContext + # which is stopped now, we need to renew the instantiated SparkSession. + # Otherwise, we will use invalid SparkSession when we call Builder.getOrCreate. + if SparkSession._instantiatedSession is None \ + or SparkSession._instantiatedSession._sc._jsc is None: + SparkSession._instantiatedSession = self + self._jvm.SparkSession.setDefaultSession(self._jsparkSession) + + def _repr_html_(self): + return """ +
+

SparkSession - {catalogImplementation}

+ {sc_HTML} +
+ """.format( + catalogImplementation=self.conf.get("spark.sql.catalogImplementation"), + sc_HTML=self.sparkContext._repr_html_() + ) + + @since(2.0) + def newSession(self): + """ + Returns a new SparkSession as new session, that has separate SQLConf, + registered temporary views and UDFs, but shared SparkContext and + table cache. + """ + return self.__class__(self._sc, self._jsparkSession.newSession()) + + @property + @since(2.0) + def sparkContext(self): + """Returns the underlying :class:`SparkContext`.""" + return self._sc + + @property + @since(2.0) + def version(self): + """The version of Spark on which this application is running.""" + return self._jsparkSession.version() + + @property + @since(2.0) + def conf(self): + """Runtime configuration interface for Spark. + + This is the interface through which the user can get and set all Spark and Hadoop + configurations that are relevant to Spark SQL. When getting the value of a config, + this defaults to the value set in the underlying :class:`SparkContext`, if any. + """ + if not hasattr(self, "_conf"): + self._conf = RuntimeConfig(self._jsparkSession.conf()) + return self._conf + + @property + @since(2.0) + def catalog(self): + """Interface through which the user may create, drop, alter or query underlying + databases, tables, functions etc. + + :return: :class:`Catalog` + """ + from pyspark.sql.catalog import Catalog + if not hasattr(self, "_catalog"): + self._catalog = Catalog(self) + return self._catalog + + @property + @since(2.0) + def udf(self): + """Returns a :class:`UDFRegistration` for UDF registration. + + :return: :class:`UDFRegistration` + """ + from pyspark.sql.udf import UDFRegistration + return UDFRegistration(self) + + @since(2.0) + def range(self, start, end=None, step=1, numPartitions=None): + """ + Create a :class:`DataFrame` with single :class:`pyspark.sql.types.LongType` column named + ``id``, containing elements in a range from ``start`` to ``end`` (exclusive) with + step value ``step``. + + :param start: the start value + :param end: the end value (exclusive) + :param step: the incremental step (default: 1) + :param numPartitions: the number of partitions of the DataFrame + :return: :class:`DataFrame` + + >>> spark.range(1, 7, 2).collect() + [Row(id=1), Row(id=3), Row(id=5)] + + If only one argument is specified, it will be used as the end value. + + >>> spark.range(3).collect() + [Row(id=0), Row(id=1), Row(id=2)] + """ + if numPartitions is None: + numPartitions = self._sc.defaultParallelism + + if end is None: + jdf = self._jsparkSession.range(0, int(start), int(step), int(numPartitions)) + else: + jdf = self._jsparkSession.range(int(start), int(end), int(step), int(numPartitions)) + + return DataFrame(jdf, self._wrapped) + + def _inferSchemaFromList(self, data, names=None): + """ + Infer schema from list of Row or tuple. + + :param data: list of Row or tuple + :param names: list of column names + :return: :class:`pyspark.sql.types.StructType` + """ + if not data: + raise ValueError("can not infer schema from empty dataset") + first = data[0] + if type(first) is dict: + warnings.warn("inferring schema from dict is deprecated," + "please use pyspark.sql.Row instead") + schema = reduce(_merge_type, (_infer_schema(row, names) for row in data)) + if _has_nulltype(schema): + raise ValueError("Some of types cannot be determined after inferring") + return schema + + def _inferSchema(self, rdd, samplingRatio=None, names=None): + """ + Infer schema from an RDD of Row or tuple. + + :param rdd: an RDD of Row or tuple + :param samplingRatio: sampling ratio, or no sampling (default) + :return: :class:`pyspark.sql.types.StructType` + """ + first = rdd.first() + if not first: + raise ValueError("The first row in RDD is empty, " + "can not infer schema") + if type(first) is dict: + warnings.warn("Using RDD of dict to inferSchema is deprecated. " + "Use pyspark.sql.Row instead") + + if samplingRatio is None: + schema = _infer_schema(first, names=names) + if _has_nulltype(schema): + for row in rdd.take(100)[1:]: + schema = _merge_type(schema, _infer_schema(row, names=names)) + if not _has_nulltype(schema): + break + else: + raise ValueError("Some of types cannot be determined by the " + "first 100 rows, please try again with sampling") + else: + if samplingRatio < 0.99: + rdd = rdd.sample(False, float(samplingRatio)) + schema = rdd.map(lambda row: _infer_schema(row, names)).reduce(_merge_type) + return schema + + def _createFromRDD(self, rdd, schema, samplingRatio): + """ + Create an RDD for DataFrame from an existing RDD, returns the RDD and schema. + """ + if schema is None or isinstance(schema, (list, tuple)): + struct = self._inferSchema(rdd, samplingRatio, names=schema) + converter = _create_converter(struct) + rdd = rdd.map(converter) + if isinstance(schema, (list, tuple)): + for i, name in enumerate(schema): + struct.fields[i].name = name + struct.names[i] = name + schema = struct + + elif not isinstance(schema, StructType): + raise TypeError("schema should be StructType or list or None, but got: %s" % schema) + + # convert python objects to sql data + rdd = rdd.map(schema.toInternal) + return rdd, schema + + def _createFromLocal(self, data, schema): + """ + Create an RDD for DataFrame from a list or pandas.DataFrame, returns + the RDD and schema. + """ + # make sure data could consumed multiple times + if not isinstance(data, list): + data = list(data) + + if schema is None or isinstance(schema, (list, tuple)): + struct = self._inferSchemaFromList(data, names=schema) + converter = _create_converter(struct) + data = map(converter, data) + if isinstance(schema, (list, tuple)): + for i, name in enumerate(schema): + struct.fields[i].name = name + struct.names[i] = name + schema = struct + + elif not isinstance(schema, StructType): + raise TypeError("schema should be StructType or list or None, but got: %s" % schema) + + # convert python objects to sql data + data = [schema.toInternal(row) for row in data] + return self._sc.parallelize(data), schema + + def _get_numpy_record_dtype(self, rec): + """ + Used when converting a pandas.DataFrame to Spark using to_records(), this will correct + the dtypes of fields in a record so they can be properly loaded into Spark. + :param rec: a numpy record to check field dtypes + :return corrected dtype for a numpy.record or None if no correction needed + """ + import numpy as np + cur_dtypes = rec.dtype + col_names = cur_dtypes.names + record_type_list = [] + has_rec_fix = False + for i in xrange(len(cur_dtypes)): + curr_type = cur_dtypes[i] + # If type is a datetime64 timestamp, convert to microseconds + # NOTE: if dtype is datetime[ns] then np.record.tolist() will output values as longs, + # conversion from [us] or lower will lead to py datetime objects, see SPARK-22417 + if curr_type == np.dtype('datetime64[ns]'): + curr_type = 'datetime64[us]' + has_rec_fix = True + record_type_list.append((str(col_names[i]), curr_type)) + return np.dtype(record_type_list) if has_rec_fix else None + + def _convert_from_pandas(self, pdf, schema, timezone): + """ + Convert a pandas.DataFrame to list of records that can be used to make a DataFrame + :return list of records + """ + if timezone is not None: + from pyspark.sql.types import _check_series_convert_timestamps_tz_local + copied = False + if isinstance(schema, StructType): + for field in schema: + # TODO: handle nested timestamps, such as ArrayType(TimestampType())? + if isinstance(field.dataType, TimestampType): + s = _check_series_convert_timestamps_tz_local(pdf[field.name], timezone) + if s is not pdf[field.name]: + if not copied: + # Copy once if the series is modified to prevent the original + # Pandas DataFrame from being updated + pdf = pdf.copy() + copied = True + pdf[field.name] = s + else: + for column, series in pdf.iteritems(): + s = _check_series_convert_timestamps_tz_local(series, timezone) + if s is not series: + if not copied: + # Copy once if the series is modified to prevent the original + # Pandas DataFrame from being updated + pdf = pdf.copy() + copied = True + pdf[column] = s + + # Convert pandas.DataFrame to list of numpy records + np_records = pdf.to_records(index=False) + + # Check if any columns need to be fixed for Spark to infer properly + if len(np_records) > 0: + record_dtype = self._get_numpy_record_dtype(np_records[0]) + if record_dtype is not None: + return [r.astype(record_dtype).tolist() for r in np_records] + + # Convert list of numpy records to python lists + return [r.tolist() for r in np_records] + + def _create_from_pandas_with_arrow(self, pdf, schema, timezone): + """ + Create a DataFrame from a given pandas.DataFrame by slicing it into partitions, converting + to Arrow data, then sending to the JVM to parallelize. If a schema is passed in, the + data types will be used to coerce the data in Pandas to Arrow conversion. + """ + from pyspark.serializers import ArrowStreamSerializer, _create_batch + from pyspark.sql.types import from_arrow_schema, to_arrow_type, TimestampType + from pyspark.sql.utils import require_minimum_pandas_version, \ + require_minimum_pyarrow_version + + require_minimum_pandas_version() + require_minimum_pyarrow_version() + + from pandas.api.types import is_datetime64_dtype, is_datetime64tz_dtype + + # Determine arrow types to coerce data when creating batches + if isinstance(schema, StructType): + arrow_types = [to_arrow_type(f.dataType) for f in schema.fields] + elif isinstance(schema, DataType): + raise ValueError("Single data type %s is not supported with Arrow" % str(schema)) + else: + # Any timestamps must be coerced to be compatible with Spark + arrow_types = [to_arrow_type(TimestampType()) + if is_datetime64_dtype(t) or is_datetime64tz_dtype(t) else None + for t in pdf.dtypes] + + # Slice the DataFrame to be batched + step = -(-len(pdf) // self.sparkContext.defaultParallelism) # round int up + pdf_slices = (pdf[start:start + step] for start in xrange(0, len(pdf), step)) + + # Create Arrow record batches + batches = [_create_batch([(c, t) for (_, c), t in zip(pdf_slice.iteritems(), arrow_types)], + timezone) + for pdf_slice in pdf_slices] + + # Create the Spark schema from the first Arrow batch (always at least 1 batch after slicing) + if isinstance(schema, (list, tuple)): + struct = from_arrow_schema(batches[0].schema) + for i, name in enumerate(schema): + struct.fields[i].name = name + struct.names[i] = name + schema = struct + + jsqlContext = self._wrapped._jsqlContext + + def reader_func(temp_filename): + return self._jvm.PythonSQLUtils.readArrowStreamFromFile(jsqlContext, temp_filename) + + def create_RDD_server(): + return self._jvm.ArrowRDDServer(jsqlContext) + + # Create Spark DataFrame from Arrow stream file, using one batch per partition + jrdd = self._sc._serialize_to_jvm(batches, ArrowStreamSerializer(), reader_func, + create_RDD_server) + jdf = self._jvm.PythonSQLUtils.toDataFrame(jrdd, schema.json(), jsqlContext) + df = DataFrame(jdf, self._wrapped) + df._schema = schema + return df + + @staticmethod + def _create_shell_session(): + """ + Initialize a SparkSession for a pyspark shell session. This is called from shell.py + to make error handling simpler without needing to declare local variables in that + script, which would expose those to users. + """ + import py4j + from pyspark.conf import SparkConf + from pyspark.context import SparkContext + try: + # Try to access HiveConf, it will raise exception if Hive is not added + conf = SparkConf() + if conf.get('spark.sql.catalogImplementation', 'hive').lower() == 'hive': + SparkContext._jvm.org.apache.hadoop.hive.conf.HiveConf() + return SparkSession.builder\ + .enableHiveSupport()\ + .getOrCreate() + else: + return SparkSession.builder.getOrCreate() + except (py4j.protocol.Py4JError, TypeError): + if conf.get('spark.sql.catalogImplementation', '').lower() == 'hive': + warnings.warn("Fall back to non-hive support because failing to access HiveConf, " + "please make sure you build spark with hive") + + return SparkSession.builder.getOrCreate() + + @since(2.0) + @ignore_unicode_prefix + def createDataFrame(self, data, schema=None, samplingRatio=None, verifySchema=True): + """ + Creates a :class:`DataFrame` from an :class:`RDD`, a list or a :class:`pandas.DataFrame`. + + When ``schema`` is a list of column names, the type of each column + will be inferred from ``data``. + + When ``schema`` is ``None``, it will try to infer the schema (column names and types) + from ``data``, which should be an RDD of :class:`Row`, + or :class:`namedtuple`, or :class:`dict`. + + When ``schema`` is :class:`pyspark.sql.types.DataType` or a datatype string, it must match + the real data, or an exception will be thrown at runtime. If the given schema is not + :class:`pyspark.sql.types.StructType`, it will be wrapped into a + :class:`pyspark.sql.types.StructType` as its only field, and the field name will be "value", + each record will also be wrapped into a tuple, which can be converted to row later. + + If schema inference is needed, ``samplingRatio`` is used to determined the ratio of + rows used for schema inference. The first row will be used if ``samplingRatio`` is ``None``. + + :param data: an RDD of any kind of SQL data representation(e.g. row, tuple, int, boolean, + etc.), or :class:`list`, or :class:`pandas.DataFrame`. + :param schema: a :class:`pyspark.sql.types.DataType` or a datatype string or a list of + column names, default is ``None``. The data type string format equals to + :class:`pyspark.sql.types.DataType.simpleString`, except that top level struct type can + omit the ``struct<>`` and atomic types use ``typeName()`` as their format, e.g. use + ``byte`` instead of ``tinyint`` for :class:`pyspark.sql.types.ByteType`. We can also use + ``int`` as a short name for ``IntegerType``. + :param samplingRatio: the sample ratio of rows used for inferring + :param verifySchema: verify data types of every row against schema. + :return: :class:`DataFrame` + + .. versionchanged:: 2.1 + Added verifySchema. + + .. note:: Usage with spark.sql.execution.arrow.enabled=True is experimental. + + >>> l = [('Alice', 1)] + >>> spark.createDataFrame(l).collect() + [Row(_1=u'Alice', _2=1)] + >>> spark.createDataFrame(l, ['name', 'age']).collect() + [Row(name=u'Alice', age=1)] + + >>> d = [{'name': 'Alice', 'age': 1}] + >>> spark.createDataFrame(d).collect() + [Row(age=1, name=u'Alice')] + + >>> rdd = sc.parallelize(l) + >>> spark.createDataFrame(rdd).collect() + [Row(_1=u'Alice', _2=1)] + >>> df = spark.createDataFrame(rdd, ['name', 'age']) + >>> df.collect() + [Row(name=u'Alice', age=1)] + + >>> from pyspark.sql import Row + >>> Person = Row('name', 'age') + >>> person = rdd.map(lambda r: Person(*r)) + >>> df2 = spark.createDataFrame(person) + >>> df2.collect() + [Row(name=u'Alice', age=1)] + + >>> from pyspark.sql.types import * + >>> schema = StructType([ + ... StructField("name", StringType(), True), + ... StructField("age", IntegerType(), True)]) + >>> df3 = spark.createDataFrame(rdd, schema) + >>> df3.collect() + [Row(name=u'Alice', age=1)] + + >>> spark.createDataFrame(df.toPandas()).collect() # doctest: +SKIP + [Row(name=u'Alice', age=1)] + >>> spark.createDataFrame(pandas.DataFrame([[1, 2]])).collect() # doctest: +SKIP + [Row(0=1, 1=2)] + + >>> spark.createDataFrame(rdd, "a: string, b: int").collect() + [Row(a=u'Alice', b=1)] + >>> rdd = rdd.map(lambda row: row[1]) + >>> spark.createDataFrame(rdd, "int").collect() + [Row(value=1)] + >>> spark.createDataFrame(rdd, "boolean").collect() # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + Py4JJavaError: ... + """ + if isinstance(data, DataFrame): + raise TypeError("data is already a DataFrame") + + if isinstance(schema, basestring): + schema = _parse_datatype_string(schema) + elif isinstance(schema, (list, tuple)): + # Must re-encode any unicode strings to be consistent with StructField names + schema = [x.encode('utf-8') if not isinstance(x, str) else x for x in schema] + + try: + import pandas + has_pandas = True + except Exception: + has_pandas = False + if has_pandas and isinstance(data, pandas.DataFrame): + from pyspark.sql.utils import require_minimum_pandas_version + require_minimum_pandas_version() + + if self._wrapped._conf.pandasRespectSessionTimeZone(): + timezone = self._wrapped._conf.sessionLocalTimeZone() + else: + timezone = None + + # If no schema supplied by user then get the names of columns only + if schema is None: + schema = [str(x) if not isinstance(x, basestring) else + (x.encode('utf-8') if not isinstance(x, str) else x) + for x in data.columns] + + if self._wrapped._conf.arrowEnabled() and len(data) > 0: + try: + return self._create_from_pandas_with_arrow(data, schema, timezone) + except Exception as e: + from pyspark.util import _exception_message + + if self._wrapped._conf.arrowFallbackEnabled(): + msg = ( + "createDataFrame attempted Arrow optimization because " + "'spark.sql.execution.arrow.enabled' is set to true; however, " + "failed by the reason below:\n %s\n" + "Attempting non-optimization as " + "'spark.sql.execution.arrow.fallback.enabled' is set to " + "true." % _exception_message(e)) + warnings.warn(msg) + else: + msg = ( + "createDataFrame attempted Arrow optimization because " + "'spark.sql.execution.arrow.enabled' is set to true, but has reached " + "the error below and will not continue because automatic fallback " + "with 'spark.sql.execution.arrow.fallback.enabled' has been set to " + "false.\n %s" % _exception_message(e)) + warnings.warn(msg) + raise + data = self._convert_from_pandas(data, schema, timezone) + + if isinstance(schema, StructType): + verify_func = _make_type_verifier(schema) if verifySchema else lambda _: True + + def prepare(obj): + verify_func(obj) + return obj + elif isinstance(schema, DataType): + dataType = schema + schema = StructType().add("value", schema) + + verify_func = _make_type_verifier( + dataType, name="field value") if verifySchema else lambda _: True + + def prepare(obj): + verify_func(obj) + return obj, + else: + prepare = lambda obj: obj + + if isinstance(data, RDD): + rdd, schema = self._createFromRDD(data.map(prepare), schema, samplingRatio) + else: + rdd, schema = self._createFromLocal(map(prepare, data), schema) + jrdd = self._jvm.SerDeUtil.toJavaArray(rdd._to_java_object_rdd()) + jdf = self._jsparkSession.applySchemaToPythonRDD(jrdd.rdd(), schema.json()) + df = DataFrame(jdf, self._wrapped) + df._schema = schema + return df + + @ignore_unicode_prefix + @since(2.0) + def sql(self, sqlQuery): + """Returns a :class:`DataFrame` representing the result of the given query. + + :return: :class:`DataFrame` + + >>> df.createOrReplaceTempView("table1") + >>> df2 = spark.sql("SELECT field1 AS f1, field2 as f2 from table1") + >>> df2.collect() + [Row(f1=1, f2=u'row1'), Row(f1=2, f2=u'row2'), Row(f1=3, f2=u'row3')] + """ + return DataFrame(self._jsparkSession.sql(sqlQuery), self._wrapped) + + @since(2.0) + def table(self, tableName): + """Returns the specified table as a :class:`DataFrame`. + + :return: :class:`DataFrame` + + >>> df.createOrReplaceTempView("table1") + >>> df2 = spark.table("table1") + >>> sorted(df.collect()) == sorted(df2.collect()) + True + """ + return DataFrame(self._jsparkSession.table(tableName), self._wrapped) + + @property + @since(2.0) + def read(self): + """ + Returns a :class:`DataFrameReader` that can be used to read data + in as a :class:`DataFrame`. + + :return: :class:`DataFrameReader` + """ + return DataFrameReader(self._wrapped) + + @property + @since(2.0) + def readStream(self): + """ + Returns a :class:`DataStreamReader` that can be used to read data streams + as a streaming :class:`DataFrame`. + + .. note:: Evolving. + + :return: :class:`DataStreamReader` + """ + return DataStreamReader(self._wrapped) + + @property + @since(2.0) + def streams(self): + """Returns a :class:`StreamingQueryManager` that allows managing all the + :class:`StreamingQuery` StreamingQueries active on `this` context. + + .. note:: Evolving. + + :return: :class:`StreamingQueryManager` + """ + from pyspark.sql.streaming import StreamingQueryManager + return StreamingQueryManager(self._jsparkSession.streams()) + + @since(2.0) + def stop(self): + """Stop the underlying :class:`SparkContext`. + """ + self._sc.stop() + # We should clean the default session up. See SPARK-23228. + self._jvm.SparkSession.clearDefaultSession() + SparkSession._instantiatedSession = None + + @since(2.0) + def __enter__(self): + """ + Enable 'with SparkSession.builder.(...).getOrCreate() as session: app' syntax. + """ + return self + + @since(2.0) + def __exit__(self, exc_type, exc_val, exc_tb): + """ + Enable 'with SparkSession.builder.(...).getOrCreate() as session: app' syntax. + + Specifically stop the SparkSession on exit of the with block. + """ + self.stop() + + +def _test(): + import os + import doctest + from pyspark.context import SparkContext + from pyspark.sql import Row + import pyspark.sql.session + + os.chdir(os.environ["SPARK_HOME"]) + + globs = pyspark.sql.session.__dict__.copy() + sc = SparkContext('local[4]', 'PythonTest') + globs['sc'] = sc + globs['spark'] = SparkSession(sc) + globs['rdd'] = rdd = sc.parallelize( + [Row(field1=1, field2="row1"), + Row(field1=2, field2="row2"), + Row(field1=3, field2="row3")]) + globs['df'] = rdd.toDF() + (failure_count, test_count) = doctest.testmod( + pyspark.sql.session, globs=globs, + optionflags=doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE) + globs['sc'].stop() + if failure_count: + sys.exit(-1) + +if __name__ == "__main__": + _test() diff --git a/docker/tidb/tispark/tispark-tests/tests/loaddata.sh b/docker/tidb/tispark/tispark-tests/tests/loaddata.sh new file mode 100755 index 0000000..8bea607 --- /dev/null +++ b/docker/tidb/tispark/tispark-tests/tests/loaddata.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +cd /opt/spark/data/tispark-sample-data + +mysql -h tidb -P 4000 -u root < dss.ddl diff --git a/docker/tidb/tispark/tispark-tests/tests/tests.py b/docker/tidb/tispark/tispark-tests/tests/tests.py new file mode 100755 index 0000000..76aa403 --- /dev/null +++ b/docker/tidb/tispark/tispark-tests/tests/tests.py @@ -0,0 +1,9 @@ +from pyspark.sql import SparkSession + +spark = SparkSession.builder.master("spark://tispark-master:7077").appName("TiSpark tests").getOrCreate() + +spark.sql("use TPCH_001") + +count = spark.sql("select count(*) as c from lineitem").first()['c'] + +assert 60175 == count diff --git a/docker/tidb/tools/container_debug b/docker/tidb/tools/container_debug new file mode 100755 index 0000000..890ffbe --- /dev/null +++ b/docker/tidb/tools/container_debug @@ -0,0 +1,190 @@ +#!/bin/bash + +# Copyright 2018 PingCAP, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# See the License for the specific language governing permissions and +# limitations under the License. + +#* --------------------------------------------------------------------- */ +#* log configure */ +#* --------------------------------------------------------------------- */ +### Define logging color +COLOR_ORIGIN="\033[0m" +COLOR_GREEN="\033[32m" +COLOR_YELLOW="\033[33m" +COLOR_RED="\033[31m" + +### Define logging level +LOGGER_LEVEL="3" + +### Define common logger +function logger() { + cur_level=$1 + cur_type=$2 + cur_color=$3 + shift && shift && shift + cur_msg=$* + + [[ ${LOGGER_LEVEL} -lt ${cur_level} ]] && return 0 + + pre_fix="${cur_color}[${cur_type}][$(date +%F)][$(date +%T)]" + pos_fix="${COLOR_ORIGIN}" + echo -e "${pre_fix} ${cur_msg} ${pos_fix}" +} + +### Define notice logger +function notice() { + logger 3 "NOTICE" ${COLOR_GREEN} $* +} + +### Define warning logger +function warning() { + logger 2 "WARNING" ${COLOR_YELLOW} $* +} + +### Define fatal logger +function fatal() { + logger 1 "FATAL" ${COLOR_RED} $* + exit 1 +} +########################################################################## + +function print_help() { +echo "\ +${1:-Debug tool for container.} + +Usage: + container_debug [OPTIONS] [ARG] + +Options: + -i The container's identity, possible values are 'containerID' or 'containerName' + -s The service name defined in docker-compose + -w Run pprof via a web interface for go program + -p The binary path of the debugged process in its own container + -h Print help infomation + +When you enter the debug container, you can find the pid of the debugged process through the ps command, +then you can find the binary of the debugged process through this path /proc/\${pid}/root/\${binary_path}. + +\${binary_path} represents the binary path of the debugged process in its own container. +\${pid} represents the process id of the debugged process as seen in the debug container. +" >&2 +exit +} + +###############################variable define################################## +WORKSPACE=$(cd $(dirname $0)/..; pwd) +DEBUG_IMAGE=${DEBUG_IMAGE:-uhub.service.ucloud.cn/pingcap/tidb-debug:latest} +SUFFIX=$(uuidgen|cut -d'-' -f1|tr '[A-Z]' '[a-z]') +DEBUG_CONTAINER_NAME=debug-${SUFFIX} +TMP_FILE=$(mktemp /tmp/binary.XXXXXX) +################################################################################ + +if [[ $# -eq 0 ]] +then + print_help +fi + +function cleanup() { + notice "start to clean tmp file ${TMP_FILE}" + [[ -f ${TMP_FILE} ]] && rm -f ${TMP_FILE} +} + +### register signal processing function +trap cleanup EXIT + +### change workspace +cd $WORKSPACE + +optstring=":i:s:p:wh" + +while getopts "$optstring" opt; do + case $opt in + i) + container_id=${OPTARG} + ;; + s) + service_name=${OPTARG} + ;; + p) + binary_path=${OPTARG} + ;; + w) + web=true + ;; + h) + print_help + ;; + \?) + fatal "Invalid option: -$OPTARG" >&2 + ;; + :) + fatal "Option -$OPTARG requires an argument" >&2 + ;; + esac +done + +if [[ -z ${service_name} && -z ${container_id} ]] +then + fatal "please use -s or -i options to select the target container" >&2 +elif [[ ! -z ${container_id} ]] +then + ### If both -s and -i options are specified, the -i option is preferred + cid=${container_id} +else + cprefix=$(basename $(pwd)|tr -Cd '[A-Za-z0-9]'|tr '[A-Z]' '[a-z]') + cid="${cprefix}_${service_name}_1" + docker ps | grep ${cid} >/dev/null + [[ $? -ne 0 ]] && fatal "not found docker-compose service ${service_name}, please confirm the correct docker-compose service name" >&2 +fi + +if [[ ! -z ${binary_path} ]] +then + binary_name=$(basename ${binary_path}) + docker cp ${cid}:${binary_path} ${TMP_FILE} + if [[ $? -ne 0 ]] + then + ### not found binary in container, reset variable ${binary_name} + binary_name= + warning "not found ${binary_path} in container ${cid}, please specify the correct binary path in container" >&2 + fi +fi + +if [[ ! -z ${web} ]] +then + ### starts a web server for graphic visualizations of golang program profiles + + ### generate a random web port + ### TODO: Test whether this port has been used + wport=${RANDOM} + [[ ${wport} -lt 10000 ]] && wport=$((wport+10000)) + + ### get the container exposed port + cport=$(docker port ${cid}|grep -E '[0-9]{5}'|awk -F: '{print $NF}') + notice "starts a web server on localhost:${wport}" + pprof -http=:${wport} ${TMP_FILE} http://localhost:${cport}/debug/pprof/profile +else + ### enter debug container to debug the specified container + docker_run_args=(-ti --rm --name=${DEBUG_CONTAINER_NAME}) + docker_run_args+=(--pid=container:${cid}) + docker_run_args+=(--network=container:${cid}) + docker_run_args+=(--ipc=container:${cid}) + docker_run_args+=(--cap-add=SYS_PTRACE) + docker_run_args+=(--privileged=true) + if [[ ! -z ${binary_name} && -e ${TMP_FILE} ]] + then + docker_run_args+=(-v ${TMP_FILE}:/${binary_name}) + else + notice "you can access the debugged container ${cid} file system through this path /proc/\${DEBUGGED_PROCESS_PID}/root" + fi + docker_run_args+=($DEBUG_IMAGE) + docker run ${docker_run_args[@]} +fi diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..cf4c18e --- /dev/null +++ b/docs/README.md @@ -0,0 +1,53 @@ +代码安全指南: +https://github.com/Tencent/secguide + +本地 dns 系统: +https://github.com/mafintosh/dns-discovery + +https://github.com/cloudwu/skynet/issues/288 + +启动服务器流程要理清楚(架构图) + +凡是 道具生成都是 一套 掉落系统 + +分布式 事件系统 + +属性 同步 + +aoi 服务 独立处理 做为 大脑来用 它还同时调度 所有 战斗 地图 +相关 服务的 分布式 时钟(tick) + +aoe 技能的拾取是 一帧内的下一tick 处理 以引用计数来统计 +是否已经达到下一步的要求 + +数据库操作 key 都以 crc16 算法确定链路 而且都是 encode 完再 +发到 对应的db 服 这样会减少2次 打包 table的 开销 +并且 数据缓存 需要 按 只查询 和 可读写来划分隔离出来2部分通过 +lru 来管理 要不然会把 老数据残留的问题 + +战斗 都以 buff 来处理 +当然不能局限于战斗,本身就是一种设计局限了 +抱着这个思想,天然就无法应对策划千奇百怪的需求 +就看怎么理解的了. 有的人认为buff是战斗专用的. +但有的人认为buff只是对状态的抽象. +而且传统的组队认证在底层跟用buff基本一样。而且用buff更灵活 +人家魔兽世界万物皆技能或Buff 开个门都是buff +https://blog.codingnow.com/2007/11/inertia_thinking.html + +逻辑状态在哪 复杂度就在哪边 + +启动配置 需要 分为 开发 和 发布 两种 + + +- 切换增量GC: `collectgarbage("incremental", pause, stepmul, stepsize)` + - pause 间歇率,垃圾收集器间歇率控制着收集器需要在开启新的循环前要等待多久。 增大这个值会减少收集器的积极性。 当这个值比 100 小的时候,收集器在开启新的循环前不会有等待。 设置这个值为 200 就会让收集器等到总内存使用量达到 之前的两倍时才开始新的循环。 + - stepmul 垃圾收集器步进倍率控制着收集器运作速度相对于内存分配速度的倍率。 增大这个值不仅会让收集器更加积极,还会增加每个增量步骤的长度。 不要把这个值设得小于 100 , 那样的话收集器就工作的太慢了以至于永远都干不完一个循环。 默认值是 200 ,这表示收集器以内存分配的“两倍”速工作。 + - stepsize 步长“大小”由 arg 控制。 传入 0 时,收集器步进(不可分割的)一步。 传入非 0 值, 收集器收集相当于 Lua 分配这些多(K 字节)内存的工作。 + 这三个参数只对 增量 有用 + + +- 切换分代GC: `collectgarbage("generational", minor_multiplier, major_multiplier)` + - minor_multiplier 控制年轻对象收集倍率,对于一个倍率`X`, 新的`minor collection`将会在内存使用比上次`major collection`后增长`X%`时完成. 默认值是20,最大值200 + - major_multiplier 控制年老对象收集倍率,对于一个倍率`X`, 新的`major collection`将会在内存使用比上次`major collection`后增长`X%`时完成. 默认值是100,最大值1000 + + 分代GC 是用这两个参数控制 \ No newline at end of file diff --git a/docs/lua-style-guide/README.md b/docs/lua-style-guide/README.md new file mode 100755 index 0000000..84c7d39 --- /dev/null +++ b/docs/lua-style-guide/README.md @@ -0,0 +1,834 @@ +# Lua Style Guide + +This style guide contains a list of guidelines that we try to follow for our +projects. It does not attempt to make arguments for the styles; its goal is +to provide consistency across projects. + +Feel free to fork this style guide and change to your own +liking, and file issues / pull requests if you have questions, comments, or if +you find any mistakes or typos. + + +## Table of Contents + + 1. [Types](#types) + 1. [Tables](#tables) + 1. [Strings](#strings) + 1. [Functions](#functions) + 1. [Properties](#properties) + 1. [Variables](#variables) + 1. [Conditional Expressions & Equality](#conditionals) + 1. [Blocks](#blocks) + 1. [Whitespace](#whitespace) + 1. [Commas](#commas) + 1. [Semicolons](#semicolons) + 1. [Type Casting & Coercion](#type-coercion) + 1. [Naming Conventions](#naming-conventions) + 1. [Accessors](#accessors) + 1. [Constructors](#constructors) + 1. [Modules](#modules) + 1. [File Structure](#file-structure) + 1. [Testing](#testing) + 1. [Performance](#performance) + 1. [Resources](#resources) + 1. [In the Wild](#in-the-wild) + 1. [Contributors](#contributors) + 1. [License](#license) + +## Types + + - **Primitives**: When you access a primitive type you work directly on its value + + + `string` + + `number` + + `boolean` + + `nil` + + ```lua + local foo = 1 + local bar = foo + + bar = 9 + + print(foo, bar) -- => 1 9 + ``` + + - **Complex**: When you access a complex type you work on a reference to its value + + + `table` + + `function` + + `userdata` + + ```lua + local foo = { 1, 2 } + local bar = foo + + bar[0] = 9 + foo[1] = 3 + + print(foo[0], bar[0]) -- => 9 9 + print(foo[1], bar[1]) -- => 3 3 + print(foo[2], bar[2]) -- => 2 2 + ``` + + **[[⬆]](#TOC)** + +## Tables + + - Use the constructor syntax for table property creation where possible. + + ```lua + -- bad + local player = {} + player.name = 'Jack' + player.class = 'Rogue' + + -- good + local player = { + name = 'Jack', + class = 'Rogue' + } + ``` + + - Define functions externally to table definition. + + ```lua + -- bad + local player = { + attack = function() + -- ...stuff... + end + } + + -- good + local function attack() + end + + local player = { + attack = attack + } + ``` + + - Consider `nil` properties when selecting lengths. + A good idea is to store an `n` property on lists that contain the length + (as noted in [Storing Nils in Tables](http://lua-users.org/wiki/StoringNilsInTables)) + + ```lua + -- nils don't count + local list = {} + list[0] = nil + list[1] = 'item' + + print(#list) -- 0 + print(select('#', list)) -- 1 + ``` + + - When tables have functions, use `self` when referring to itself. + + ```lua + -- bad + local me = { + fullname = function(this) + return this.first_name + ' ' + this.last_name + end + } + + -- good + local me = { + fullname = function(self) + return self.first_name + ' ' + self.last_name + end + } + ``` + + **[[⬆]](#TOC)** + +## Strings + + - Use single quotes `''` for strings. + + ```lua + -- bad + local name = "Bob Parr" + + -- good + local name = 'Bob Parr' + + -- bad + local fullName = "Bob " .. self.lastName + + -- good + local fullName = 'Bob ' .. self.lastName + ``` + + - Strings longer than 80 characters should be written across multiple lines + using concatenation. This allows you to indent nicely. + + ```lua + -- bad + local errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.' + + -- bad + local errorMessage = 'This is a super long error that \ + was thrown because of Batman. \ + When you stop to think about \ + how Batman had anything to do \ + with this, you would get nowhere \ + fast.' + + + -- bad + local errorMessage = [[This is a super long error that + was thrown because of Batman. + When you stop to think about + how Batman had anything to do + with this, you would get nowhere + fast.]] + + -- good + local errorMessage = 'This is a super long error that ' .. + 'was thrown because of Batman. ' .. + 'When you stop to think about ' .. + 'how Batman had anything to do ' .. + 'with this, you would get nowhere ' .. + 'fast.' + ``` + + **[[⬆]](#TOC)** + + +## Functions + - Prefer lots of small functions to large, complex functions. [Smalls Functions Are Good For The Universe](http://kikito.github.io/blog/2012/03/16/small-functions-are-good-for-the-universe/). + + - Prefer function syntax over variable syntax. This helps differentiate + between named and anonymous functions. + + ```lua + -- bad + local nope = function(name, options) + -- ...stuff... + end + + -- good + local function yup(name, options) + -- ...stuff... + end + ``` + + - Never name a parameter `arg`, this will take precendence over the `arg` object that is given to every function scope in older versions of Lua. + + ```lua + -- bad + local function nope(name, options, arg) + -- ...stuff... + end + + -- good + local function yup(name, options, ...) + -- ...stuff... + end + ``` + + - Perform validation early and return as early as possible. + + ```lua + -- bad + local is_good_name = function(name, options, arg) + local is_good = #name > 3 + is_good = is_good and #name < 30 + + -- ...stuff... + + return is_bad + end + + -- good + local is_good_name = function(name, options, args) + if #name < 3 or #name > 30 then return false end + + -- ...stuff... + + return true + end + ``` + + **[[⬆]](#TOC)** + + +## Properties + + - Use dot notation when accessing known properties. + + ```lua + local luke = { + jedi = true, + age = 28 + } + + -- bad + local isJedi = luke['jedi'] + + -- good + local isJedi = luke.jedi + ``` + + - Use subscript notation `[]` when accessing properties with a variable + or if using a table as a list. + + ```lua + local luke = { + jedi = true, + age = 28 + } + + local function getProp(prop) + return luke[prop] + end + + local isJedi = getProp('jedi') + ``` + + **[[⬆]](#TOC)** + + +## Variables + + - Always use `local` to declare variables. Not doing so will result in + global variables to avoid polluting the global namespace. + + ```lua + -- bad + superPower = SuperPower() + + -- good + local superPower = SuperPower() + ``` + + - Assign variables at the top of their scope where possible. This makes it + easier to check for existing variables. + + ```lua + -- bad + local bad = function() + test() + print('doing stuff..') + + //..other stuff.. + + local name = getName() + + if name == 'test' then + return false + end + + return name + end + + -- good + local function good() + local name = getName() + + test() + print('doing stuff..') + + //..other stuff.. + + if name == 'test' then + return false + end + + return name + end + ``` + + **[[⬆]](#TOC)** + + +## Conditional Expressions & Equality + + - False and nil are *falsy* in conditional expressions. All else is true. + + ```lua + local str = '' + + if str then + -- true + end + ``` + + - Use shortcuts when you can, unless you need to know the difference between + false and nil. + + ```lua + -- bad + if name ~= nil then + -- ...stuff... + end + + -- good + if name then + -- ...stuff... + end + ``` + + - Prefer *true* statements over *false* statements where it makes sense. + Prioritize truthy conditions when writing multiple conditions. + + ```lua + --bad + if not thing then + -- ...stuff... + else + -- ...stuff... + end + + --good + if thing then + -- ...stuff... + else + -- ...stuff... + end + ``` + + - Prefer defaults to `else` statements where it makes sense. This results in + less complex and safer code at the expense of variable reassignment, so + situations may differ. + + ```lua + --bad + local function full_name(first, last) + local name + + if first and last then + name = first .. ' ' .. last + else + name = 'John Smith' + end + + return name + end + + --good + local function full_name(first, last) + local name = 'John Smith' + + if first and last then + name = first .. ' ' .. last + end + + return name + end + ``` + + - Short ternaries are okay. + + ```lua + local function default_name(name) + -- return the default 'Waldo' if name is nil + return name or 'Waldo' + end + + local function brew_coffee(machine) + return machine and machine.is_loaded and 'coffee brewing' or 'fill your water' + end + ``` + + + **[[⬆]](#TOC)** + + +## Blocks + + - Single line blocks are okay for *small* statements. Try to keep lines to 80 characters. + Indent lines if they overflow past the limit. + + ```lua + -- good + if test then return false end + + -- good + if test then + return false + end + + -- bad + if test < 1 and do_complicated_function(test) == false or seven == 8 and nine == 10 then do_other_complicated_function()end + + -- good + if test < 1 and do_complicated_function(test) == false or + seven == 8 and nine == 10 then + + do_other_complicated_function() + return false + end + ``` + + **[[⬆]](#TOC)** + + +## Whitespace + + - Use soft tabs set to 2 spaces. Tab characters and 4-space tabs result in public flogging. + + ```lua + -- bad + function() + ∙∙∙∙local name + end + + -- bad + function() + ∙local name + end + + -- good + function() + ∙∙local name + end + ``` + + - Place 1 space before opening and closing braces. Place no spaces around parens. + + ```lua + -- bad + local test = {one=1} + + -- good + local test = { one = 1 } + + -- bad + dog.set('attr',{ + age = '1 year', + breed = 'Bernese Mountain Dog' + }) + + -- good + dog.set('attr', { + age = '1 year', + breed = 'Bernese Mountain Dog' + }) + ``` + + - Place an empty newline at the end of the file. + + ```lua + -- bad + (function(global) + -- ...stuff... + end)(self) + ``` + + ```lua + -- good + (function(global) + -- ...stuff... + end)(self) + + ``` + + - Surround operators with spaces. + + ```lua + -- bad + local thing=1 + thing = thing-1 + thing = thing*1 + thing = 'string'..'s' + + -- good + local thing = 1 + thing = thing - 1 + thing = thing * 1 + thing = 'string' .. 's' + ``` + + - Use one space after commas. + + ```lua + --bad + local thing = {1,2,3} + thing = {1 , 2 , 3} + thing = {1 ,2 ,3} + + --good + local thing = {1, 2, 3} + ``` + + - Add a line break after multiline blocks. + + ```lua + --bad + if thing then + -- ...stuff... + end + function derp() + -- ...stuff... + end + local wat = 7 + + --good + if thing then + -- ...stuff... + end + + function derp() + -- ...stuff... + end + + local wat = 7 + ``` + + - Delete unnecessary whitespace at the end of lines. + + **[[⬆]](#TOC)** + +## Commas + + - Leading commas aren't okay. An ending comma on the last item is okay but discouraged. + + ```lua + -- bad + local thing = { + once = 1 + , upon = 2 + , aTime = 3 + } + + -- good + local thing = { + once = 1, + upon = 2, + aTime = 3 + } + + -- okay + local thing = { + once = 1, + upon = 2, + aTime = 3, + } + ``` + + **[[⬆]](#TOC)** + + +## Semicolons + + - **Nope.** Separate statements onto multiple lines. + + ```lua + -- bad + local whatever = 'sure'; + a = 1; b = 2 + + -- good + local whatever = 'sure' + a = 1 + b = 2 + ``` + + **[[⬆]](#TOC)** + + +## Type Casting & Coercion + + - Perform type coercion at the beginning of the statement. Use the built-in functions. (`tostring`, `tonumber`, etc.) + + - Use `tostring` for strings if you need to cast without string concatenation. + + ```lua + -- bad + local totalScore = reviewScore .. '' + + -- good + local totalScore = tostring(reviewScore) + ``` + + - Use `tonumber` for Numbers. + + ```lua + local inputValue = '4' + + -- bad + local val = inputValue * 1 + + -- good + local val = tonumber(inputValue) + ``` + + **[[⬆]](#TOC)** + + +## Naming Conventions + + - Avoid single letter names. Be descriptive with your naming. You can get + away with single-letter names when they are variables in loops. + + ```lua + -- bad + local function q() + -- ...stuff... + end + + -- good + local function query() + -- ..stuff.. + end + ``` + + - Use underscores for ignored variables in loops. + + ```lua + --good + for _, name in pairs(names) do + -- ...stuff... + end + ``` + + - Use snake_case when naming objects, functions, and instances. Tend towards + verbosity if unsure about naming. + + ```lua + -- bad + local OBJEcttsssss = {} + local thisIsMyObject = {} + local this-is-my-object = {} + + local c = function() + -- ...stuff... + end + + -- good + local this_is_my_object = {} + + local function do_that_thing() + -- ...stuff... + end + ``` + + - Use PascalCase for factories. + + ```lua + -- bad + local player = require('player') + + -- good + local Player = require('player') + local me = Player({ name = 'Jack' }) + ``` + + **[[⬆]](#TOC)** + + - Use `is` or `has` for boolean-returning functions that are part of tables. + + ```lua + --bad + local function evil(alignment) + return alignment < 100 + end + + --good + local function is_evil(alignment) + return alignment < 100 + end + ``` + +## Modules + + - The module should return a table or function. + - The module should not use the global namespace for anything ever. The + module should be a closure. + - The file should be named like the module. + + ```lua + -- thing.lua + local thing = { } + + local meta = { + __call = function(self, key, vars) + print key + end + } + + + return setmetatable(thing, meta) + ``` + + - Note that modules are [loaded as singletons](http://lua-users.org/wiki/TheEssenceOfLoadingCode) + and therefore should usually be factories (a function returning a new instance of a table) + unless static (like utility libraries.) + + **[[⬆]](#TOC)** + +## File Structure + + - Files should be named in all lowercase. + - Lua files should be in a top-level `src` folder. The main library file should + be called `modulename.lua`. + - Rockspecs, license, readme, etc should be in the top level. + - Tests should be in a top-level `spec` folder. + - Executables should be in a top-level `bin` folder. + - Example: + + ``` + ./my_module + bin/ + script.sh + + spec/ + my_module_spec.lua + some_file.lua + + src/ + my_module.lua + some_file.lua + + README.md + LICENSE.md + ``` + +## Testing + + - Use [busted](http://olivinelabs.com/busted) and write lots of tests in a /spec + folder. Separate tests by module. + - Use descriptive `describe` and `it` blocks so it's obvious to see what + precisely is failing. + - Test interfaces. Don't test private methods. If you need to test something + that is private, it probably shouldn't be private in the first place. + - Example: + + ``` + ./my_module + bin/ + script.sh + + spec/ + my_module_spec.lua + + util/ + formatters_spec.lua + + src/ + my_module.lua + + util/ + formatters.lua + + README.md + LICENSE.md + ``` + + **[[⬆]](#TOC)** + +## Contributors + + - [View contributors](https://github.com/Olivine-Labs/lua-style-guide/graphs/contributors) + + **[[⬆]](#TOC)** + +## License + + - Released under CC0 (Public Domain). + Information can be found at [http://creativecommons.org/publicdomain/zero/1.0/](http://creativecommons.org/publicdomain/zero/1.0/). + +**[[⬆]](#TOC)** diff --git a/framework/3rd/argon2/include/argon2.h b/framework/3rd/argon2/include/argon2.h new file mode 100755 index 0000000..3980bb3 --- /dev/null +++ b/framework/3rd/argon2/include/argon2.h @@ -0,0 +1,437 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ARGON2_H +#define ARGON2_H + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Symbols visibility control */ +#ifdef A2_VISCTL +#define ARGON2_PUBLIC __attribute__((visibility("default"))) +#define ARGON2_LOCAL __attribute__ ((visibility ("hidden"))) +#elif defined(_MSC_VER) +#define ARGON2_PUBLIC __declspec(dllexport) +#define ARGON2_LOCAL +#else +#define ARGON2_PUBLIC +#define ARGON2_LOCAL +#endif + +/* + * Argon2 input parameter restrictions + */ + +/* Minimum and maximum number of lanes (degree of parallelism) */ +#define ARGON2_MIN_LANES UINT32_C(1) +#define ARGON2_MAX_LANES UINT32_C(0xFFFFFF) + +/* Minimum and maximum number of threads */ +#define ARGON2_MIN_THREADS UINT32_C(1) +#define ARGON2_MAX_THREADS UINT32_C(0xFFFFFF) + +/* Number of synchronization points between lanes per pass */ +#define ARGON2_SYNC_POINTS UINT32_C(4) + +/* Minimum and maximum digest size in bytes */ +#define ARGON2_MIN_OUTLEN UINT32_C(4) +#define ARGON2_MAX_OUTLEN UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum number of memory blocks (each of BLOCK_SIZE bytes) */ +#define ARGON2_MIN_MEMORY (2 * ARGON2_SYNC_POINTS) /* 2 blocks per slice */ + +#define ARGON2_MIN(a, b) ((a) < (b) ? (a) : (b)) +/* Max memory size is addressing-space/2, topping at 2^32 blocks (4 TB) */ +#define ARGON2_MAX_MEMORY_BITS \ + ARGON2_MIN(UINT32_C(32), (sizeof(void *) * CHAR_BIT - 10 - 1)) +#define ARGON2_MAX_MEMORY \ + ARGON2_MIN(UINT32_C(0xFFFFFFFF), UINT64_C(1) << ARGON2_MAX_MEMORY_BITS) + +/* Minimum and maximum number of passes */ +#define ARGON2_MIN_TIME UINT32_C(1) +#define ARGON2_MAX_TIME UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum password length in bytes */ +#define ARGON2_MIN_PWD_LENGTH UINT32_C(0) +#define ARGON2_MAX_PWD_LENGTH UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum associated data length in bytes */ +#define ARGON2_MIN_AD_LENGTH UINT32_C(0) +#define ARGON2_MAX_AD_LENGTH UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum salt length in bytes */ +#define ARGON2_MIN_SALT_LENGTH UINT32_C(8) +#define ARGON2_MAX_SALT_LENGTH UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum key length in bytes */ +#define ARGON2_MIN_SECRET UINT32_C(0) +#define ARGON2_MAX_SECRET UINT32_C(0xFFFFFFFF) + +/* Flags to determine which fields are securely wiped (default = no wipe). */ +#define ARGON2_DEFAULT_FLAGS UINT32_C(0) +#define ARGON2_FLAG_CLEAR_PASSWORD (UINT32_C(1) << 0) +#define ARGON2_FLAG_CLEAR_SECRET (UINT32_C(1) << 1) + +/* Global flag to determine if we are wiping internal memory buffers. This flag + * is defined in core.c and defaults to 1 (wipe internal memory). */ +extern int FLAG_clear_internal_memory; + +/* Error codes */ +typedef enum Argon2_ErrorCodes { + ARGON2_OK = 0, + + ARGON2_OUTPUT_PTR_NULL = -1, + + ARGON2_OUTPUT_TOO_SHORT = -2, + ARGON2_OUTPUT_TOO_LONG = -3, + + ARGON2_PWD_TOO_SHORT = -4, + ARGON2_PWD_TOO_LONG = -5, + + ARGON2_SALT_TOO_SHORT = -6, + ARGON2_SALT_TOO_LONG = -7, + + ARGON2_AD_TOO_SHORT = -8, + ARGON2_AD_TOO_LONG = -9, + + ARGON2_SECRET_TOO_SHORT = -10, + ARGON2_SECRET_TOO_LONG = -11, + + ARGON2_TIME_TOO_SMALL = -12, + ARGON2_TIME_TOO_LARGE = -13, + + ARGON2_MEMORY_TOO_LITTLE = -14, + ARGON2_MEMORY_TOO_MUCH = -15, + + ARGON2_LANES_TOO_FEW = -16, + ARGON2_LANES_TOO_MANY = -17, + + ARGON2_PWD_PTR_MISMATCH = -18, /* NULL ptr with non-zero length */ + ARGON2_SALT_PTR_MISMATCH = -19, /* NULL ptr with non-zero length */ + ARGON2_SECRET_PTR_MISMATCH = -20, /* NULL ptr with non-zero length */ + ARGON2_AD_PTR_MISMATCH = -21, /* NULL ptr with non-zero length */ + + ARGON2_MEMORY_ALLOCATION_ERROR = -22, + + ARGON2_FREE_MEMORY_CBK_NULL = -23, + ARGON2_ALLOCATE_MEMORY_CBK_NULL = -24, + + ARGON2_INCORRECT_PARAMETER = -25, + ARGON2_INCORRECT_TYPE = -26, + + ARGON2_OUT_PTR_MISMATCH = -27, + + ARGON2_THREADS_TOO_FEW = -28, + ARGON2_THREADS_TOO_MANY = -29, + + ARGON2_MISSING_ARGS = -30, + + ARGON2_ENCODING_FAIL = -31, + + ARGON2_DECODING_FAIL = -32, + + ARGON2_THREAD_FAIL = -33, + + ARGON2_DECODING_LENGTH_FAIL = -34, + + ARGON2_VERIFY_MISMATCH = -35 +} argon2_error_codes; + +/* Memory allocator types --- for external allocation */ +typedef int (*allocate_fptr)(uint8_t **memory, size_t bytes_to_allocate); +typedef void (*deallocate_fptr)(uint8_t *memory, size_t bytes_to_allocate); + +/* Argon2 external data structures */ + +/* + ***** + * Context: structure to hold Argon2 inputs: + * output array and its length, + * password and its length, + * salt and its length, + * secret and its length, + * associated data and its length, + * number of passes, amount of used memory (in KBytes, can be rounded up a bit) + * number of parallel threads that will be run. + * All the parameters above affect the output hash value. + * Additionally, two function pointers can be provided to allocate and + * deallocate the memory (if NULL, memory will be allocated internally). + * Also, three flags indicate whether to erase password, secret as soon as they + * are pre-hashed (and thus not needed anymore), and the entire memory + ***** + * Simplest situation: you have output array out[8], password is stored in + * pwd[32], salt is stored in salt[16], you do not have keys nor associated + * data. You need to spend 1 GB of RAM and you run 5 passes of Argon2d with + * 4 parallel lanes. + * You want to erase the password, but you're OK with last pass not being + * erased. You want to use the default memory allocator. + * Then you initialize: + Argon2_Context(out,8,pwd,32,salt,16,NULL,0,NULL,0,5,1<<20,4,4,NULL,NULL,true,false,false,false) + */ +typedef struct Argon2_Context { + uint8_t *out; /* output array */ + uint32_t outlen; /* digest length */ + + uint8_t *pwd; /* password array */ + uint32_t pwdlen; /* password length */ + + uint8_t *salt; /* salt array */ + uint32_t saltlen; /* salt length */ + + uint8_t *secret; /* key array */ + uint32_t secretlen; /* key length */ + + uint8_t *ad; /* associated data array */ + uint32_t adlen; /* associated data length */ + + uint32_t t_cost; /* number of passes */ + uint32_t m_cost; /* amount of memory requested (KB) */ + uint32_t lanes; /* number of lanes */ + uint32_t threads; /* maximum number of threads */ + + uint32_t version; /* version number */ + + allocate_fptr allocate_cbk; /* pointer to memory allocator */ + deallocate_fptr free_cbk; /* pointer to memory deallocator */ + + uint32_t flags; /* array of bool options */ +} argon2_context; + +/* Argon2 primitive type */ +typedef enum Argon2_type { + Argon2_d = 0, + Argon2_i = 1, + Argon2_id = 2 +} argon2_type; + +/* Version of the algorithm */ +typedef enum Argon2_version { + ARGON2_VERSION_10 = 0x10, + ARGON2_VERSION_13 = 0x13, + ARGON2_VERSION_NUMBER = ARGON2_VERSION_13 +} argon2_version; + +/* + * Function that gives the string representation of an argon2_type. + * @param type The argon2_type that we want the string for + * @param uppercase Whether the string should have the first letter uppercase + * @return NULL if invalid type, otherwise the string representation. + */ +ARGON2_PUBLIC const char *argon2_type2string(argon2_type type, int uppercase); + +/* + * Function that performs memory-hard hashing with certain degree of parallelism + * @param context Pointer to the Argon2 internal structure + * @return Error code if smth is wrong, ARGON2_OK otherwise + */ +ARGON2_PUBLIC int argon2_ctx(argon2_context *context, argon2_type type); + +/** + * Hashes a password with Argon2i, producing an encoded hash + * @param t_cost Number of iterations + * @param m_cost Sets memory usage to m_cost kibibytes + * @param parallelism Number of threads and compute lanes + * @param pwd Pointer to password + * @param pwdlen Password size in bytes + * @param salt Pointer to salt + * @param saltlen Salt size in bytes + * @param hashlen Desired length of the hash in bytes + * @param encoded Buffer where to write the encoded hash + * @param encodedlen Size of the buffer (thus max size of the encoded hash) + * @pre Different parallelism levels will give different results + * @pre Returns ARGON2_OK if successful + */ +ARGON2_PUBLIC int argon2i_hash_encoded(const uint32_t t_cost, + const uint32_t m_cost, + const uint32_t parallelism, + const void *pwd, const size_t pwdlen, + const void *salt, const size_t saltlen, + const size_t hashlen, char *encoded, + const size_t encodedlen); + +/** + * Hashes a password with Argon2i, producing a raw hash at @hash + * @param t_cost Number of iterations + * @param m_cost Sets memory usage to m_cost kibibytes + * @param parallelism Number of threads and compute lanes + * @param pwd Pointer to password + * @param pwdlen Password size in bytes + * @param salt Pointer to salt + * @param saltlen Salt size in bytes + * @param hash Buffer where to write the raw hash - updated by the function + * @param hashlen Desired length of the hash in bytes + * @pre Different parallelism levels will give different results + * @pre Returns ARGON2_OK if successful + */ +ARGON2_PUBLIC int argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, + const size_t hashlen); + +ARGON2_PUBLIC int argon2d_hash_encoded(const uint32_t t_cost, + const uint32_t m_cost, + const uint32_t parallelism, + const void *pwd, const size_t pwdlen, + const void *salt, const size_t saltlen, + const size_t hashlen, char *encoded, + const size_t encodedlen); + +ARGON2_PUBLIC int argon2d_hash_raw(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, + const size_t hashlen); + +ARGON2_PUBLIC int argon2id_hash_encoded(const uint32_t t_cost, + const uint32_t m_cost, + const uint32_t parallelism, + const void *pwd, const size_t pwdlen, + const void *salt, const size_t saltlen, + const size_t hashlen, char *encoded, + const size_t encodedlen); + +ARGON2_PUBLIC int argon2id_hash_raw(const uint32_t t_cost, + const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, + const size_t hashlen); + +/* generic function underlying the above ones */ +ARGON2_PUBLIC int argon2_hash(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, + const size_t hashlen, char *encoded, + const size_t encodedlen, argon2_type type, + const uint32_t version); + +/** + * Verifies a password against an encoded string + * Encoded string is restricted as in validate_inputs() + * @param encoded String encoding parameters, salt, hash + * @param pwd Pointer to password + * @pre Returns ARGON2_OK if successful + */ +ARGON2_PUBLIC int argon2i_verify(const char *encoded, const void *pwd, + const size_t pwdlen); + +ARGON2_PUBLIC int argon2d_verify(const char *encoded, const void *pwd, + const size_t pwdlen); + +ARGON2_PUBLIC int argon2id_verify(const char *encoded, const void *pwd, + const size_t pwdlen); + +/* generic function underlying the above ones */ +ARGON2_PUBLIC int argon2_verify(const char *encoded, const void *pwd, + const size_t pwdlen, argon2_type type); + +/** + * Argon2d: Version of Argon2 that picks memory blocks depending + * on the password and salt. Only for side-channel-free + * environment!! + ***** + * @param context Pointer to current Argon2 context + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2d_ctx(argon2_context *context); + +/** + * Argon2i: Version of Argon2 that picks memory blocks + * independent on the password and salt. Good for side-channels, + * but worse w.r.t. tradeoff attacks if only one pass is used. + ***** + * @param context Pointer to current Argon2 context + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2i_ctx(argon2_context *context); + +/** + * Argon2id: Version of Argon2 where the first half-pass over memory is + * password-independent, the rest are password-dependent (on the password and + * salt). OK against side channels (they reduce to 1/2-pass Argon2i), and + * better with w.r.t. tradeoff attacks (similar to Argon2d). + ***** + * @param context Pointer to current Argon2 context + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2id_ctx(argon2_context *context); + +/** + * Verify if a given password is correct for Argon2d hashing + * @param context Pointer to current Argon2 context + * @param hash The password hash to verify. The length of the hash is + * specified by the context outlen member + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2d_verify_ctx(argon2_context *context, const char *hash); + +/** + * Verify if a given password is correct for Argon2i hashing + * @param context Pointer to current Argon2 context + * @param hash The password hash to verify. The length of the hash is + * specified by the context outlen member + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2i_verify_ctx(argon2_context *context, const char *hash); + +/** + * Verify if a given password is correct for Argon2id hashing + * @param context Pointer to current Argon2 context + * @param hash The password hash to verify. The length of the hash is + * specified by the context outlen member + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2id_verify_ctx(argon2_context *context, + const char *hash); + +/* generic function underlying the above ones */ +ARGON2_PUBLIC int argon2_verify_ctx(argon2_context *context, const char *hash, + argon2_type type); + +/** + * Get the associated error message for given error code + * @return The error message associated with the given error code + */ +ARGON2_PUBLIC const char *argon2_error_message(int error_code); + +/** + * Returns the encoded hash length for the given input parameters + * @param t_cost Number of iterations + * @param m_cost Memory usage in kibibytes + * @param parallelism Number of threads; used to compute lanes + * @param saltlen Salt size in bytes + * @param hashlen Hash size in bytes + * @param type The argon2_type that we want the encoded length for + * @return The encoded hash length in bytes + */ +ARGON2_PUBLIC size_t argon2_encodedlen(uint32_t t_cost, uint32_t m_cost, + uint32_t parallelism, uint32_t saltlen, + uint32_t hashlen, argon2_type type); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/framework/3rd/argon2/src/argon2.c b/framework/3rd/argon2/src/argon2.c new file mode 100755 index 0000000..34da3d6 --- /dev/null +++ b/framework/3rd/argon2/src/argon2.c @@ -0,0 +1,452 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include + +#include "argon2.h" +#include "encoding.h" +#include "core.h" + +const char *argon2_type2string(argon2_type type, int uppercase) { + switch (type) { + case Argon2_d: + return uppercase ? "Argon2d" : "argon2d"; + case Argon2_i: + return uppercase ? "Argon2i" : "argon2i"; + case Argon2_id: + return uppercase ? "Argon2id" : "argon2id"; + } + + return NULL; +} + +int argon2_ctx(argon2_context *context, argon2_type type) { + /* 1. Validate all inputs */ + int result = validate_inputs(context); + uint32_t memory_blocks, segment_length; + argon2_instance_t instance; + + if (ARGON2_OK != result) { + return result; + } + + if (Argon2_d != type && Argon2_i != type && Argon2_id != type) { + return ARGON2_INCORRECT_TYPE; + } + + /* 2. Align memory size */ + /* Minimum memory_blocks = 8L blocks, where L is the number of lanes */ + memory_blocks = context->m_cost; + + if (memory_blocks < 2 * ARGON2_SYNC_POINTS * context->lanes) { + memory_blocks = 2 * ARGON2_SYNC_POINTS * context->lanes; + } + + segment_length = memory_blocks / (context->lanes * ARGON2_SYNC_POINTS); + /* Ensure that all segments have equal length */ + memory_blocks = segment_length * (context->lanes * ARGON2_SYNC_POINTS); + + instance.version = context->version; + instance.memory = NULL; + instance.passes = context->t_cost; + instance.memory_blocks = memory_blocks; + instance.segment_length = segment_length; + instance.lane_length = segment_length * ARGON2_SYNC_POINTS; + instance.lanes = context->lanes; + instance.threads = context->threads; + instance.type = type; + + if (instance.threads > instance.lanes) { + instance.threads = instance.lanes; + } + + /* 3. Initialization: Hashing inputs, allocating memory, filling first + * blocks + */ + result = initialize(&instance, context); + + if (ARGON2_OK != result) { + return result; + } + + /* 4. Filling memory */ + result = fill_memory_blocks(&instance); + + if (ARGON2_OK != result) { + return result; + } + /* 5. Finalization */ + finalize(context, &instance); + + return ARGON2_OK; +} + +int argon2_hash(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, const size_t saltlen, + void *hash, const size_t hashlen, char *encoded, + const size_t encodedlen, argon2_type type, + const uint32_t version){ + + argon2_context context; + int result; + uint8_t *out; + + if (pwdlen > ARGON2_MAX_PWD_LENGTH) { + return ARGON2_PWD_TOO_LONG; + } + + if (saltlen > ARGON2_MAX_SALT_LENGTH) { + return ARGON2_SALT_TOO_LONG; + } + + if (hashlen > ARGON2_MAX_OUTLEN) { + return ARGON2_OUTPUT_TOO_LONG; + } + + if (hashlen < ARGON2_MIN_OUTLEN) { + return ARGON2_OUTPUT_TOO_SHORT; + } + + out = malloc(hashlen); + if (!out) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + context.out = (uint8_t *)out; + context.outlen = (uint32_t)hashlen; + context.pwd = CONST_CAST(uint8_t *)pwd; + context.pwdlen = (uint32_t)pwdlen; + context.salt = CONST_CAST(uint8_t *)salt; + context.saltlen = (uint32_t)saltlen; + context.secret = NULL; + context.secretlen = 0; + context.ad = NULL; + context.adlen = 0; + context.t_cost = t_cost; + context.m_cost = m_cost; + context.lanes = parallelism; + context.threads = parallelism; + context.allocate_cbk = NULL; + context.free_cbk = NULL; + context.flags = ARGON2_DEFAULT_FLAGS; + context.version = version; + + result = argon2_ctx(&context, type); + + if (result != ARGON2_OK) { + clear_internal_memory(out, hashlen); + free(out); + return result; + } + + /* if raw hash requested, write it */ + if (hash) { + memcpy(hash, out, hashlen); + } + + /* if encoding requested, write it */ + if (encoded && encodedlen) { + if (encode_string(encoded, encodedlen, &context, type) != ARGON2_OK) { + clear_internal_memory(out, hashlen); /* wipe buffers if error */ + clear_internal_memory(encoded, encodedlen); + free(out); + return ARGON2_ENCODING_FAIL; + } + } + clear_internal_memory(out, hashlen); + free(out); + + return ARGON2_OK; +} + +int argon2i_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, const size_t hashlen, + char *encoded, const size_t encodedlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + NULL, hashlen, encoded, encodedlen, Argon2_i, + ARGON2_VERSION_NUMBER); +} + +int argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, const size_t hashlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + hash, hashlen, NULL, 0, Argon2_i, ARGON2_VERSION_NUMBER); +} + +int argon2d_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, const size_t hashlen, + char *encoded, const size_t encodedlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + NULL, hashlen, encoded, encodedlen, Argon2_d, + ARGON2_VERSION_NUMBER); +} + +int argon2d_hash_raw(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, const size_t hashlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + hash, hashlen, NULL, 0, Argon2_d, ARGON2_VERSION_NUMBER); +} + +int argon2id_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, const size_t hashlen, + char *encoded, const size_t encodedlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + NULL, hashlen, encoded, encodedlen, Argon2_id, + ARGON2_VERSION_NUMBER); +} + +int argon2id_hash_raw(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, const size_t hashlen) { + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + hash, hashlen, NULL, 0, Argon2_id, + ARGON2_VERSION_NUMBER); +} + +static int argon2_compare(const uint8_t *b1, const uint8_t *b2, size_t len) { + size_t i; + uint8_t d = 0U; + + for (i = 0U; i < len; i++) { + d |= b1[i] ^ b2[i]; + } + return (int)((1 & ((d - 1) >> 8)) - 1); +} + +int argon2_verify(const char *encoded, const void *pwd, const size_t pwdlen, + argon2_type type) { + + argon2_context ctx; + uint8_t *desired_result = NULL; + + int ret = ARGON2_OK; + + size_t encoded_len; + uint32_t max_field_len; + + if (pwdlen > ARGON2_MAX_PWD_LENGTH) { + return ARGON2_PWD_TOO_LONG; + } + + if (encoded == NULL) { + return ARGON2_DECODING_FAIL; + } + + encoded_len = strlen(encoded); + if (encoded_len > UINT32_MAX) { + return ARGON2_DECODING_FAIL; + } + + /* No field can be longer than the encoded length */ + max_field_len = (uint32_t)encoded_len; + + ctx.saltlen = max_field_len; + ctx.outlen = max_field_len; + + ctx.salt = malloc(ctx.saltlen); + ctx.out = malloc(ctx.outlen); + if (!ctx.salt || !ctx.out) { + ret = ARGON2_MEMORY_ALLOCATION_ERROR; + goto fail; + } + + ctx.pwd = (uint8_t *)pwd; + ctx.pwdlen = (uint32_t)pwdlen; + + ret = decode_string(&ctx, encoded, type); + if (ret != ARGON2_OK) { + goto fail; + } + + /* Set aside the desired result, and get a new buffer. */ + desired_result = ctx.out; + ctx.out = malloc(ctx.outlen); + if (!ctx.out) { + ret = ARGON2_MEMORY_ALLOCATION_ERROR; + goto fail; + } + + ret = argon2_verify_ctx(&ctx, (char *)desired_result, type); + if (ret != ARGON2_OK) { + goto fail; + } + +fail: + free(ctx.salt); + free(ctx.out); + free(desired_result); + + return ret; +} + +int argon2i_verify(const char *encoded, const void *pwd, const size_t pwdlen) { + + return argon2_verify(encoded, pwd, pwdlen, Argon2_i); +} + +int argon2d_verify(const char *encoded, const void *pwd, const size_t pwdlen) { + + return argon2_verify(encoded, pwd, pwdlen, Argon2_d); +} + +int argon2id_verify(const char *encoded, const void *pwd, const size_t pwdlen) { + + return argon2_verify(encoded, pwd, pwdlen, Argon2_id); +} + +int argon2d_ctx(argon2_context *context) { + return argon2_ctx(context, Argon2_d); +} + +int argon2i_ctx(argon2_context *context) { + return argon2_ctx(context, Argon2_i); +} + +int argon2id_ctx(argon2_context *context) { + return argon2_ctx(context, Argon2_id); +} + +int argon2_verify_ctx(argon2_context *context, const char *hash, + argon2_type type) { + int ret = argon2_ctx(context, type); + if (ret != ARGON2_OK) { + return ret; + } + + if (argon2_compare((uint8_t *)hash, context->out, context->outlen)) { + return ARGON2_VERIFY_MISMATCH; + } + + return ARGON2_OK; +} + +int argon2d_verify_ctx(argon2_context *context, const char *hash) { + return argon2_verify_ctx(context, hash, Argon2_d); +} + +int argon2i_verify_ctx(argon2_context *context, const char *hash) { + return argon2_verify_ctx(context, hash, Argon2_i); +} + +int argon2id_verify_ctx(argon2_context *context, const char *hash) { + return argon2_verify_ctx(context, hash, Argon2_id); +} + +const char *argon2_error_message(int error_code) { + switch (error_code) { + case ARGON2_OK: + return "OK"; + case ARGON2_OUTPUT_PTR_NULL: + return "Output pointer is NULL"; + case ARGON2_OUTPUT_TOO_SHORT: + return "Output is too short"; + case ARGON2_OUTPUT_TOO_LONG: + return "Output is too long"; + case ARGON2_PWD_TOO_SHORT: + return "Password is too short"; + case ARGON2_PWD_TOO_LONG: + return "Password is too long"; + case ARGON2_SALT_TOO_SHORT: + return "Salt is too short"; + case ARGON2_SALT_TOO_LONG: + return "Salt is too long"; + case ARGON2_AD_TOO_SHORT: + return "Associated data is too short"; + case ARGON2_AD_TOO_LONG: + return "Associated data is too long"; + case ARGON2_SECRET_TOO_SHORT: + return "Secret is too short"; + case ARGON2_SECRET_TOO_LONG: + return "Secret is too long"; + case ARGON2_TIME_TOO_SMALL: + return "Time cost is too small"; + case ARGON2_TIME_TOO_LARGE: + return "Time cost is too large"; + case ARGON2_MEMORY_TOO_LITTLE: + return "Memory cost is too small"; + case ARGON2_MEMORY_TOO_MUCH: + return "Memory cost is too large"; + case ARGON2_LANES_TOO_FEW: + return "Too few lanes"; + case ARGON2_LANES_TOO_MANY: + return "Too many lanes"; + case ARGON2_PWD_PTR_MISMATCH: + return "Password pointer is NULL, but password length is not 0"; + case ARGON2_SALT_PTR_MISMATCH: + return "Salt pointer is NULL, but salt length is not 0"; + case ARGON2_SECRET_PTR_MISMATCH: + return "Secret pointer is NULL, but secret length is not 0"; + case ARGON2_AD_PTR_MISMATCH: + return "Associated data pointer is NULL, but ad length is not 0"; + case ARGON2_MEMORY_ALLOCATION_ERROR: + return "Memory allocation error"; + case ARGON2_FREE_MEMORY_CBK_NULL: + return "The free memory callback is NULL"; + case ARGON2_ALLOCATE_MEMORY_CBK_NULL: + return "The allocate memory callback is NULL"; + case ARGON2_INCORRECT_PARAMETER: + return "Argon2_Context context is NULL"; + case ARGON2_INCORRECT_TYPE: + return "There is no such version of Argon2"; + case ARGON2_OUT_PTR_MISMATCH: + return "Output pointer mismatch"; + case ARGON2_THREADS_TOO_FEW: + return "Not enough threads"; + case ARGON2_THREADS_TOO_MANY: + return "Too many threads"; + case ARGON2_MISSING_ARGS: + return "Missing arguments"; + case ARGON2_ENCODING_FAIL: + return "Encoding failed"; + case ARGON2_DECODING_FAIL: + return "Decoding failed"; + case ARGON2_THREAD_FAIL: + return "Threading failure"; + case ARGON2_DECODING_LENGTH_FAIL: + return "Some of encoded parameters are too long or too short"; + case ARGON2_VERIFY_MISMATCH: + return "The password does not match the supplied hash"; + default: + return "Unknown error code"; + } +} + +size_t argon2_encodedlen(uint32_t t_cost, uint32_t m_cost, uint32_t parallelism, + uint32_t saltlen, uint32_t hashlen, argon2_type type) { + return strlen("$$v=$m=,t=,p=$$") + strlen(argon2_type2string(type, 0)) + + numlen(t_cost) + numlen(m_cost) + numlen(parallelism) + + b64len(saltlen) + b64len(hashlen) + numlen(ARGON2_VERSION_NUMBER) + 1; +} diff --git a/framework/3rd/argon2/src/blake2/blake2-impl.h b/framework/3rd/argon2/src/blake2/blake2-impl.h new file mode 100755 index 0000000..86d0d5c --- /dev/null +++ b/framework/3rd/argon2/src/blake2/blake2-impl.h @@ -0,0 +1,156 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef PORTABLE_BLAKE2_IMPL_H +#define PORTABLE_BLAKE2_IMPL_H + +#include +#include + +#ifdef _WIN32 +#define BLAKE2_INLINE __inline +#elif defined(__GNUC__) || defined(__clang__) +#define BLAKE2_INLINE __inline__ +#else +#define BLAKE2_INLINE +#endif + +/* Argon2 Team - Begin Code */ +/* + Not an exhaustive list, but should cover the majority of modern platforms + Additionally, the code will always be correct---this is only a performance + tweak. +*/ +#if (defined(__BYTE_ORDER__) && \ + (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || \ + defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__MIPSEL__) || \ + defined(__AARCH64EL__) || defined(__amd64__) || defined(__i386__) || \ + defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || \ + defined(_M_ARM) +#define NATIVE_LITTLE_ENDIAN +#endif +/* Argon2 Team - End Code */ + +static BLAKE2_INLINE uint32_t load32(const void *src) { +#if defined(NATIVE_LITTLE_ENDIAN) + uint32_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = (const uint8_t *)src; + uint32_t w = *p++; + w |= (uint32_t)(*p++) << 8; + w |= (uint32_t)(*p++) << 16; + w |= (uint32_t)(*p++) << 24; + return w; +#endif +} + +static BLAKE2_INLINE uint64_t load64(const void *src) { +#if defined(NATIVE_LITTLE_ENDIAN) + uint64_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = (const uint8_t *)src; + uint64_t w = *p++; + w |= (uint64_t)(*p++) << 8; + w |= (uint64_t)(*p++) << 16; + w |= (uint64_t)(*p++) << 24; + w |= (uint64_t)(*p++) << 32; + w |= (uint64_t)(*p++) << 40; + w |= (uint64_t)(*p++) << 48; + w |= (uint64_t)(*p++) << 56; + return w; +#endif +} + +static BLAKE2_INLINE void store32(void *dst, uint32_t w) { +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; +#endif +} + +static BLAKE2_INLINE void store64(void *dst, uint64_t w) { +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; +#endif +} + +static BLAKE2_INLINE uint64_t load48(const void *src) { + const uint8_t *p = (const uint8_t *)src; + uint64_t w = *p++; + w |= (uint64_t)(*p++) << 8; + w |= (uint64_t)(*p++) << 16; + w |= (uint64_t)(*p++) << 24; + w |= (uint64_t)(*p++) << 32; + w |= (uint64_t)(*p++) << 40; + return w; +} + +static BLAKE2_INLINE void store48(void *dst, uint64_t w) { + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; +} + +static BLAKE2_INLINE uint32_t rotr32(const uint32_t w, const unsigned c) { + return (w >> c) | (w << (32 - c)); +} + +static BLAKE2_INLINE uint64_t rotr64(const uint64_t w, const unsigned c) { + return (w >> c) | (w << (64 - c)); +} + +void clear_internal_memory(void *v, size_t n); + +#endif diff --git a/framework/3rd/argon2/src/blake2/blake2.h b/framework/3rd/argon2/src/blake2/blake2.h new file mode 100755 index 0000000..501c6a3 --- /dev/null +++ b/framework/3rd/argon2/src/blake2/blake2.h @@ -0,0 +1,89 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef PORTABLE_BLAKE2_H +#define PORTABLE_BLAKE2_H + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +enum blake2b_constant { + BLAKE2B_BLOCKBYTES = 128, + BLAKE2B_OUTBYTES = 64, + BLAKE2B_KEYBYTES = 64, + BLAKE2B_SALTBYTES = 16, + BLAKE2B_PERSONALBYTES = 16 +}; + +#pragma pack(push, 1) +typedef struct __blake2b_param { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint64_t node_offset; /* 16 */ + uint8_t node_depth; /* 17 */ + uint8_t inner_length; /* 18 */ + uint8_t reserved[14]; /* 32 */ + uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ + uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ +} blake2b_param; +#pragma pack(pop) + +typedef struct __blake2b_state { + uint64_t h[8]; + uint64_t t[2]; + uint64_t f[2]; + uint8_t buf[BLAKE2B_BLOCKBYTES]; + unsigned buflen; + unsigned outlen; + uint8_t last_node; +} blake2b_state; + +/* Ensure param structs have not been wrongly padded */ +/* Poor man's static_assert */ +enum { + blake2_size_check_0 = 1 / !!(CHAR_BIT == 8), + blake2_size_check_2 = + 1 / !!(sizeof(blake2b_param) == sizeof(uint64_t) * CHAR_BIT) +}; + +/* Streaming API */ +ARGON2_LOCAL int blake2b_init(blake2b_state *S, size_t outlen); +ARGON2_LOCAL int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, + size_t keylen); +ARGON2_LOCAL int blake2b_init_param(blake2b_state *S, const blake2b_param *P); +ARGON2_LOCAL int blake2b_update(blake2b_state *S, const void *in, size_t inlen); +ARGON2_LOCAL int blake2b_final(blake2b_state *S, void *out, size_t outlen); + +/* Simple API */ +ARGON2_LOCAL int blake2b(void *out, size_t outlen, const void *in, size_t inlen, + const void *key, size_t keylen); + +/* Argon2 Team - Begin Code */ +ARGON2_LOCAL int blake2b_long(void *out, size_t outlen, const void *in, size_t inlen); +/* Argon2 Team - End Code */ + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/framework/3rd/argon2/src/blake2/blake2b.c b/framework/3rd/argon2/src/blake2/blake2b.c new file mode 100755 index 0000000..3b519dd --- /dev/null +++ b/framework/3rd/argon2/src/blake2/blake2b.c @@ -0,0 +1,390 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include + +#include "blake2.h" +#include "blake2-impl.h" + +static const uint64_t blake2b_IV[8] = { + UINT64_C(0x6a09e667f3bcc908), UINT64_C(0xbb67ae8584caa73b), + UINT64_C(0x3c6ef372fe94f82b), UINT64_C(0xa54ff53a5f1d36f1), + UINT64_C(0x510e527fade682d1), UINT64_C(0x9b05688c2b3e6c1f), + UINT64_C(0x1f83d9abfb41bd6b), UINT64_C(0x5be0cd19137e2179)}; + +static const unsigned int blake2b_sigma[12][16] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, + {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, + {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, + {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, +}; + +static BLAKE2_INLINE void blake2b_set_lastnode(blake2b_state *S) { + S->f[1] = (uint64_t)-1; +} + +static BLAKE2_INLINE void blake2b_set_lastblock(blake2b_state *S) { + if (S->last_node) { + blake2b_set_lastnode(S); + } + S->f[0] = (uint64_t)-1; +} + +static BLAKE2_INLINE void blake2b_increment_counter(blake2b_state *S, + uint64_t inc) { + S->t[0] += inc; + S->t[1] += (S->t[0] < inc); +} + +static BLAKE2_INLINE void blake2b_invalidate_state(blake2b_state *S) { + clear_internal_memory(S, sizeof(*S)); /* wipe */ + blake2b_set_lastblock(S); /* invalidate for further use */ +} + +static BLAKE2_INLINE void blake2b_init0(blake2b_state *S) { + memset(S, 0, sizeof(*S)); + memcpy(S->h, blake2b_IV, sizeof(S->h)); +} + +int blake2b_init_param(blake2b_state *S, const blake2b_param *P) { + const unsigned char *p = (const unsigned char *)P; + unsigned int i; + + if (NULL == P || NULL == S) { + return -1; + } + + blake2b_init0(S); + /* IV XOR Parameter Block */ + for (i = 0; i < 8; ++i) { + S->h[i] ^= load64(&p[i * sizeof(S->h[i])]); + } + S->outlen = P->digest_length; + return 0; +} + +/* Sequential blake2b initialization */ +int blake2b_init(blake2b_state *S, size_t outlen) { + blake2b_param P; + + if (S == NULL) { + return -1; + } + + if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + /* Setup Parameter Block for unkeyed BLAKE2 */ + P.digest_length = (uint8_t)outlen; + P.key_length = 0; + P.fanout = 1; + P.depth = 1; + P.leaf_length = 0; + P.node_offset = 0; + P.node_depth = 0; + P.inner_length = 0; + memset(P.reserved, 0, sizeof(P.reserved)); + memset(P.salt, 0, sizeof(P.salt)); + memset(P.personal, 0, sizeof(P.personal)); + + return blake2b_init_param(S, &P); +} + +int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, + size_t keylen) { + blake2b_param P; + + if (S == NULL) { + return -1; + } + + if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + if ((key == 0) || (keylen == 0) || (keylen > BLAKE2B_KEYBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + /* Setup Parameter Block for keyed BLAKE2 */ + P.digest_length = (uint8_t)outlen; + P.key_length = (uint8_t)keylen; + P.fanout = 1; + P.depth = 1; + P.leaf_length = 0; + P.node_offset = 0; + P.node_depth = 0; + P.inner_length = 0; + memset(P.reserved, 0, sizeof(P.reserved)); + memset(P.salt, 0, sizeof(P.salt)); + memset(P.personal, 0, sizeof(P.personal)); + + if (blake2b_init_param(S, &P) < 0) { + blake2b_invalidate_state(S); + return -1; + } + + { + uint8_t block[BLAKE2B_BLOCKBYTES]; + memset(block, 0, BLAKE2B_BLOCKBYTES); + memcpy(block, key, keylen); + blake2b_update(S, block, BLAKE2B_BLOCKBYTES); + /* Burn the key from stack */ + clear_internal_memory(block, BLAKE2B_BLOCKBYTES); + } + return 0; +} + +static void blake2b_compress(blake2b_state *S, const uint8_t *block) { + uint64_t m[16]; + uint64_t v[16]; + unsigned int i, r; + + for (i = 0; i < 16; ++i) { + m[i] = load64(block + i * sizeof(m[i])); + } + + for (i = 0; i < 8; ++i) { + v[i] = S->h[i]; + } + + v[8] = blake2b_IV[0]; + v[9] = blake2b_IV[1]; + v[10] = blake2b_IV[2]; + v[11] = blake2b_IV[3]; + v[12] = blake2b_IV[4] ^ S->t[0]; + v[13] = blake2b_IV[5] ^ S->t[1]; + v[14] = blake2b_IV[6] ^ S->f[0]; + v[15] = blake2b_IV[7] ^ S->f[1]; + +#define G(r, i, a, b, c, d) \ + do { \ + a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \ + d = rotr64(d ^ a, 32); \ + c = c + d; \ + b = rotr64(b ^ c, 24); \ + a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \ + d = rotr64(d ^ a, 16); \ + c = c + d; \ + b = rotr64(b ^ c, 63); \ + } while ((void)0, 0) + +#define ROUND(r) \ + do { \ + G(r, 0, v[0], v[4], v[8], v[12]); \ + G(r, 1, v[1], v[5], v[9], v[13]); \ + G(r, 2, v[2], v[6], v[10], v[14]); \ + G(r, 3, v[3], v[7], v[11], v[15]); \ + G(r, 4, v[0], v[5], v[10], v[15]); \ + G(r, 5, v[1], v[6], v[11], v[12]); \ + G(r, 6, v[2], v[7], v[8], v[13]); \ + G(r, 7, v[3], v[4], v[9], v[14]); \ + } while ((void)0, 0) + + for (r = 0; r < 12; ++r) { + ROUND(r); + } + + for (i = 0; i < 8; ++i) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } + +#undef G +#undef ROUND +} + +int blake2b_update(blake2b_state *S, const void *in, size_t inlen) { + const uint8_t *pin = (const uint8_t *)in; + + if (inlen == 0) { + return 0; + } + + /* Sanity check */ + if (S == NULL || in == NULL) { + return -1; + } + + /* Is this a reused state? */ + if (S->f[0] != 0) { + return -1; + } + + if (S->buflen + inlen > BLAKE2B_BLOCKBYTES) { + /* Complete current block */ + size_t left = S->buflen; + size_t fill = BLAKE2B_BLOCKBYTES - left; + memcpy(&S->buf[left], pin, fill); + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress(S, S->buf); + S->buflen = 0; + inlen -= fill; + pin += fill; + /* Avoid buffer copies when possible */ + while (inlen > BLAKE2B_BLOCKBYTES) { + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress(S, pin); + inlen -= BLAKE2B_BLOCKBYTES; + pin += BLAKE2B_BLOCKBYTES; + } + } + memcpy(&S->buf[S->buflen], pin, inlen); + S->buflen += (unsigned int)inlen; + return 0; +} + +int blake2b_final(blake2b_state *S, void *out, size_t outlen) { + uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; + unsigned int i; + + /* Sanity checks */ + if (S == NULL || out == NULL || outlen < S->outlen) { + return -1; + } + + /* Is this a reused state? */ + if (S->f[0] != 0) { + return -1; + } + + blake2b_increment_counter(S, S->buflen); + blake2b_set_lastblock(S); + memset(&S->buf[S->buflen], 0, BLAKE2B_BLOCKBYTES - S->buflen); /* Padding */ + blake2b_compress(S, S->buf); + + for (i = 0; i < 8; ++i) { /* Output full hash to temp buffer */ + store64(buffer + sizeof(S->h[i]) * i, S->h[i]); + } + + memcpy(out, buffer, S->outlen); + clear_internal_memory(buffer, sizeof(buffer)); + clear_internal_memory(S->buf, sizeof(S->buf)); + clear_internal_memory(S->h, sizeof(S->h)); + return 0; +} + +int blake2b(void *out, size_t outlen, const void *in, size_t inlen, + const void *key, size_t keylen) { + blake2b_state S; + int ret = -1; + + /* Verify parameters */ + if (NULL == in && inlen > 0) { + goto fail; + } + + if (NULL == out || outlen == 0 || outlen > BLAKE2B_OUTBYTES) { + goto fail; + } + + if ((NULL == key && keylen > 0) || keylen > BLAKE2B_KEYBYTES) { + goto fail; + } + + if (keylen > 0) { + if (blake2b_init_key(&S, outlen, key, keylen) < 0) { + goto fail; + } + } else { + if (blake2b_init(&S, outlen) < 0) { + goto fail; + } + } + + if (blake2b_update(&S, in, inlen) < 0) { + goto fail; + } + ret = blake2b_final(&S, out, outlen); + +fail: + clear_internal_memory(&S, sizeof(S)); + return ret; +} + +/* Argon2 Team - Begin Code */ +int blake2b_long(void *pout, size_t outlen, const void *in, size_t inlen) { + uint8_t *out = (uint8_t *)pout; + blake2b_state blake_state; + uint8_t outlen_bytes[sizeof(uint32_t)] = {0}; + int ret = -1; + + if (outlen > UINT32_MAX) { + goto fail; + } + + /* Ensure little-endian byte order! */ + store32(outlen_bytes, (uint32_t)outlen); + +#define TRY(statement) \ + do { \ + ret = statement; \ + if (ret < 0) { \ + goto fail; \ + } \ + } while ((void)0, 0) + + if (outlen <= BLAKE2B_OUTBYTES) { + TRY(blake2b_init(&blake_state, outlen)); + TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); + TRY(blake2b_update(&blake_state, in, inlen)); + TRY(blake2b_final(&blake_state, out, outlen)); + } else { + uint32_t toproduce; + uint8_t out_buffer[BLAKE2B_OUTBYTES]; + uint8_t in_buffer[BLAKE2B_OUTBYTES]; + TRY(blake2b_init(&blake_state, BLAKE2B_OUTBYTES)); + TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); + TRY(blake2b_update(&blake_state, in, inlen)); + TRY(blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES)); + memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); + out += BLAKE2B_OUTBYTES / 2; + toproduce = (uint32_t)outlen - BLAKE2B_OUTBYTES / 2; + + while (toproduce > BLAKE2B_OUTBYTES) { + memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); + TRY(blake2b(out_buffer, BLAKE2B_OUTBYTES, in_buffer, + BLAKE2B_OUTBYTES, NULL, 0)); + memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); + out += BLAKE2B_OUTBYTES / 2; + toproduce -= BLAKE2B_OUTBYTES / 2; + } + + memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); + TRY(blake2b(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL, + 0)); + memcpy(out, out_buffer, toproduce); + } +fail: + clear_internal_memory(&blake_state, sizeof(blake_state)); + return ret; +#undef TRY +} +/* Argon2 Team - End Code */ diff --git a/framework/3rd/argon2/src/blake2/blamka-round-opt.h b/framework/3rd/argon2/src/blake2/blamka-round-opt.h new file mode 100755 index 0000000..3127f2a --- /dev/null +++ b/framework/3rd/argon2/src/blake2/blamka-round-opt.h @@ -0,0 +1,471 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef BLAKE_ROUND_MKA_OPT_H +#define BLAKE_ROUND_MKA_OPT_H + +#include "blake2-impl.h" + +#include +#if defined(__SSSE3__) +#include /* for _mm_shuffle_epi8 and _mm_alignr_epi8 */ +#endif + +#if defined(__XOP__) && (defined(__GNUC__) || defined(__clang__)) +#include +#endif + +#if !defined(__AVX512F__) +#if !defined(__AVX2__) +#if !defined(__XOP__) +#if defined(__SSSE3__) +#define r16 \ + (_mm_setr_epi8(2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9)) +#define r24 \ + (_mm_setr_epi8(3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10)) +#define _mm_roti_epi64(x, c) \ + (-(c) == 32) \ + ? _mm_shuffle_epi32((x), _MM_SHUFFLE(2, 3, 0, 1)) \ + : (-(c) == 24) \ + ? _mm_shuffle_epi8((x), r24) \ + : (-(c) == 16) \ + ? _mm_shuffle_epi8((x), r16) \ + : (-(c) == 63) \ + ? _mm_xor_si128(_mm_srli_epi64((x), -(c)), \ + _mm_add_epi64((x), (x))) \ + : _mm_xor_si128(_mm_srli_epi64((x), -(c)), \ + _mm_slli_epi64((x), 64 - (-(c)))) +#else /* defined(__SSE2__) */ +#define _mm_roti_epi64(r, c) \ + _mm_xor_si128(_mm_srli_epi64((r), -(c)), _mm_slli_epi64((r), 64 - (-(c)))) +#endif +#else +#endif + +static BLAKE2_INLINE __m128i fBlaMka(__m128i x, __m128i y) { + const __m128i z = _mm_mul_epu32(x, y); + return _mm_add_epi64(_mm_add_epi64(x, y), _mm_add_epi64(z, z)); +} + +#define G1(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + A0 = fBlaMka(A0, B0); \ + A1 = fBlaMka(A1, B1); \ + \ + D0 = _mm_xor_si128(D0, A0); \ + D1 = _mm_xor_si128(D1, A1); \ + \ + D0 = _mm_roti_epi64(D0, -32); \ + D1 = _mm_roti_epi64(D1, -32); \ + \ + C0 = fBlaMka(C0, D0); \ + C1 = fBlaMka(C1, D1); \ + \ + B0 = _mm_xor_si128(B0, C0); \ + B1 = _mm_xor_si128(B1, C1); \ + \ + B0 = _mm_roti_epi64(B0, -24); \ + B1 = _mm_roti_epi64(B1, -24); \ + } while ((void)0, 0) + +#define G2(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + A0 = fBlaMka(A0, B0); \ + A1 = fBlaMka(A1, B1); \ + \ + D0 = _mm_xor_si128(D0, A0); \ + D1 = _mm_xor_si128(D1, A1); \ + \ + D0 = _mm_roti_epi64(D0, -16); \ + D1 = _mm_roti_epi64(D1, -16); \ + \ + C0 = fBlaMka(C0, D0); \ + C1 = fBlaMka(C1, D1); \ + \ + B0 = _mm_xor_si128(B0, C0); \ + B1 = _mm_xor_si128(B1, C1); \ + \ + B0 = _mm_roti_epi64(B0, -63); \ + B1 = _mm_roti_epi64(B1, -63); \ + } while ((void)0, 0) + +#if defined(__SSSE3__) +#define DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + __m128i t0 = _mm_alignr_epi8(B1, B0, 8); \ + __m128i t1 = _mm_alignr_epi8(B0, B1, 8); \ + B0 = t0; \ + B1 = t1; \ + \ + t0 = C0; \ + C0 = C1; \ + C1 = t0; \ + \ + t0 = _mm_alignr_epi8(D1, D0, 8); \ + t1 = _mm_alignr_epi8(D0, D1, 8); \ + D0 = t1; \ + D1 = t0; \ + } while ((void)0, 0) + +#define UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + __m128i t0 = _mm_alignr_epi8(B0, B1, 8); \ + __m128i t1 = _mm_alignr_epi8(B1, B0, 8); \ + B0 = t0; \ + B1 = t1; \ + \ + t0 = C0; \ + C0 = C1; \ + C1 = t0; \ + \ + t0 = _mm_alignr_epi8(D0, D1, 8); \ + t1 = _mm_alignr_epi8(D1, D0, 8); \ + D0 = t1; \ + D1 = t0; \ + } while ((void)0, 0) +#else /* SSE2 */ +#define DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + __m128i t0 = D0; \ + __m128i t1 = B0; \ + D0 = C0; \ + C0 = C1; \ + C1 = D0; \ + D0 = _mm_unpackhi_epi64(D1, _mm_unpacklo_epi64(t0, t0)); \ + D1 = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(D1, D1)); \ + B0 = _mm_unpackhi_epi64(B0, _mm_unpacklo_epi64(B1, B1)); \ + B1 = _mm_unpackhi_epi64(B1, _mm_unpacklo_epi64(t1, t1)); \ + } while ((void)0, 0) + +#define UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + __m128i t0, t1; \ + t0 = C0; \ + C0 = C1; \ + C1 = t0; \ + t0 = B0; \ + t1 = D0; \ + B0 = _mm_unpackhi_epi64(B1, _mm_unpacklo_epi64(B0, B0)); \ + B1 = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(B1, B1)); \ + D0 = _mm_unpackhi_epi64(D0, _mm_unpacklo_epi64(D1, D1)); \ + D1 = _mm_unpackhi_epi64(D1, _mm_unpacklo_epi64(t1, t1)); \ + } while ((void)0, 0) +#endif + +#define BLAKE2_ROUND(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + G1(A0, B0, C0, D0, A1, B1, C1, D1); \ + G2(A0, B0, C0, D0, A1, B1, C1, D1); \ + \ + DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1); \ + \ + G1(A0, B0, C0, D0, A1, B1, C1, D1); \ + G2(A0, B0, C0, D0, A1, B1, C1, D1); \ + \ + UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1); \ + } while ((void)0, 0) +#else /* __AVX2__ */ + +#include + +#define rotr32(x) _mm256_shuffle_epi32(x, _MM_SHUFFLE(2, 3, 0, 1)) +#define rotr24(x) _mm256_shuffle_epi8(x, _mm256_setr_epi8(3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10, 3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10)) +#define rotr16(x) _mm256_shuffle_epi8(x, _mm256_setr_epi8(2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9, 2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9)) +#define rotr63(x) _mm256_xor_si256(_mm256_srli_epi64((x), 63), _mm256_add_epi64((x), (x))) + +#define G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + __m256i ml = _mm256_mul_epu32(A0, B0); \ + ml = _mm256_add_epi64(ml, ml); \ + A0 = _mm256_add_epi64(A0, _mm256_add_epi64(B0, ml)); \ + D0 = _mm256_xor_si256(D0, A0); \ + D0 = rotr32(D0); \ + \ + ml = _mm256_mul_epu32(C0, D0); \ + ml = _mm256_add_epi64(ml, ml); \ + C0 = _mm256_add_epi64(C0, _mm256_add_epi64(D0, ml)); \ + \ + B0 = _mm256_xor_si256(B0, C0); \ + B0 = rotr24(B0); \ + \ + ml = _mm256_mul_epu32(A1, B1); \ + ml = _mm256_add_epi64(ml, ml); \ + A1 = _mm256_add_epi64(A1, _mm256_add_epi64(B1, ml)); \ + D1 = _mm256_xor_si256(D1, A1); \ + D1 = rotr32(D1); \ + \ + ml = _mm256_mul_epu32(C1, D1); \ + ml = _mm256_add_epi64(ml, ml); \ + C1 = _mm256_add_epi64(C1, _mm256_add_epi64(D1, ml)); \ + \ + B1 = _mm256_xor_si256(B1, C1); \ + B1 = rotr24(B1); \ + } while((void)0, 0); + +#define G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + __m256i ml = _mm256_mul_epu32(A0, B0); \ + ml = _mm256_add_epi64(ml, ml); \ + A0 = _mm256_add_epi64(A0, _mm256_add_epi64(B0, ml)); \ + D0 = _mm256_xor_si256(D0, A0); \ + D0 = rotr16(D0); \ + \ + ml = _mm256_mul_epu32(C0, D0); \ + ml = _mm256_add_epi64(ml, ml); \ + C0 = _mm256_add_epi64(C0, _mm256_add_epi64(D0, ml)); \ + B0 = _mm256_xor_si256(B0, C0); \ + B0 = rotr63(B0); \ + \ + ml = _mm256_mul_epu32(A1, B1); \ + ml = _mm256_add_epi64(ml, ml); \ + A1 = _mm256_add_epi64(A1, _mm256_add_epi64(B1, ml)); \ + D1 = _mm256_xor_si256(D1, A1); \ + D1 = rotr16(D1); \ + \ + ml = _mm256_mul_epu32(C1, D1); \ + ml = _mm256_add_epi64(ml, ml); \ + C1 = _mm256_add_epi64(C1, _mm256_add_epi64(D1, ml)); \ + B1 = _mm256_xor_si256(B1, C1); \ + B1 = rotr63(B1); \ + } while((void)0, 0); + +#define DIAGONALIZE_1(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + B0 = _mm256_permute4x64_epi64(B0, _MM_SHUFFLE(0, 3, 2, 1)); \ + C0 = _mm256_permute4x64_epi64(C0, _MM_SHUFFLE(1, 0, 3, 2)); \ + D0 = _mm256_permute4x64_epi64(D0, _MM_SHUFFLE(2, 1, 0, 3)); \ + \ + B1 = _mm256_permute4x64_epi64(B1, _MM_SHUFFLE(0, 3, 2, 1)); \ + C1 = _mm256_permute4x64_epi64(C1, _MM_SHUFFLE(1, 0, 3, 2)); \ + D1 = _mm256_permute4x64_epi64(D1, _MM_SHUFFLE(2, 1, 0, 3)); \ + } while((void)0, 0); + +#define DIAGONALIZE_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + __m256i tmp1 = _mm256_blend_epi32(B0, B1, 0xCC); \ + __m256i tmp2 = _mm256_blend_epi32(B0, B1, 0x33); \ + B1 = _mm256_permute4x64_epi64(tmp1, _MM_SHUFFLE(2,3,0,1)); \ + B0 = _mm256_permute4x64_epi64(tmp2, _MM_SHUFFLE(2,3,0,1)); \ + \ + tmp1 = C0; \ + C0 = C1; \ + C1 = tmp1; \ + \ + tmp1 = _mm256_blend_epi32(D0, D1, 0xCC); \ + tmp2 = _mm256_blend_epi32(D0, D1, 0x33); \ + D0 = _mm256_permute4x64_epi64(tmp1, _MM_SHUFFLE(2,3,0,1)); \ + D1 = _mm256_permute4x64_epi64(tmp2, _MM_SHUFFLE(2,3,0,1)); \ + } while(0); + +#define UNDIAGONALIZE_1(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + B0 = _mm256_permute4x64_epi64(B0, _MM_SHUFFLE(2, 1, 0, 3)); \ + C0 = _mm256_permute4x64_epi64(C0, _MM_SHUFFLE(1, 0, 3, 2)); \ + D0 = _mm256_permute4x64_epi64(D0, _MM_SHUFFLE(0, 3, 2, 1)); \ + \ + B1 = _mm256_permute4x64_epi64(B1, _MM_SHUFFLE(2, 1, 0, 3)); \ + C1 = _mm256_permute4x64_epi64(C1, _MM_SHUFFLE(1, 0, 3, 2)); \ + D1 = _mm256_permute4x64_epi64(D1, _MM_SHUFFLE(0, 3, 2, 1)); \ + } while((void)0, 0); + +#define UNDIAGONALIZE_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + __m256i tmp1 = _mm256_blend_epi32(B0, B1, 0xCC); \ + __m256i tmp2 = _mm256_blend_epi32(B0, B1, 0x33); \ + B0 = _mm256_permute4x64_epi64(tmp1, _MM_SHUFFLE(2,3,0,1)); \ + B1 = _mm256_permute4x64_epi64(tmp2, _MM_SHUFFLE(2,3,0,1)); \ + \ + tmp1 = C0; \ + C0 = C1; \ + C1 = tmp1; \ + \ + tmp1 = _mm256_blend_epi32(D0, D1, 0x33); \ + tmp2 = _mm256_blend_epi32(D0, D1, 0xCC); \ + D0 = _mm256_permute4x64_epi64(tmp1, _MM_SHUFFLE(2,3,0,1)); \ + D1 = _mm256_permute4x64_epi64(tmp2, _MM_SHUFFLE(2,3,0,1)); \ + } while((void)0, 0); + +#define BLAKE2_ROUND_1(A0, A1, B0, B1, C0, C1, D0, D1) \ + do{ \ + G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + DIAGONALIZE_1(A0, B0, C0, D0, A1, B1, C1, D1) \ + \ + G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + UNDIAGONALIZE_1(A0, B0, C0, D0, A1, B1, C1, D1) \ + } while((void)0, 0); + +#define BLAKE2_ROUND_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do{ \ + G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + DIAGONALIZE_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + UNDIAGONALIZE_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + } while((void)0, 0); + +#endif /* __AVX2__ */ + +#else /* __AVX512F__ */ + +#include + +#define ror64(x, n) _mm512_ror_epi64((x), (n)) + +static __m512i muladd(__m512i x, __m512i y) +{ + __m512i z = _mm512_mul_epu32(x, y); + return _mm512_add_epi64(_mm512_add_epi64(x, y), _mm512_add_epi64(z, z)); +} + +#define G1(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + A0 = muladd(A0, B0); \ + A1 = muladd(A1, B1); \ +\ + D0 = _mm512_xor_si512(D0, A0); \ + D1 = _mm512_xor_si512(D1, A1); \ +\ + D0 = ror64(D0, 32); \ + D1 = ror64(D1, 32); \ +\ + C0 = muladd(C0, D0); \ + C1 = muladd(C1, D1); \ +\ + B0 = _mm512_xor_si512(B0, C0); \ + B1 = _mm512_xor_si512(B1, C1); \ +\ + B0 = ror64(B0, 24); \ + B1 = ror64(B1, 24); \ + } while ((void)0, 0) + +#define G2(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + A0 = muladd(A0, B0); \ + A1 = muladd(A1, B1); \ +\ + D0 = _mm512_xor_si512(D0, A0); \ + D1 = _mm512_xor_si512(D1, A1); \ +\ + D0 = ror64(D0, 16); \ + D1 = ror64(D1, 16); \ +\ + C0 = muladd(C0, D0); \ + C1 = muladd(C1, D1); \ +\ + B0 = _mm512_xor_si512(B0, C0); \ + B1 = _mm512_xor_si512(B1, C1); \ +\ + B0 = ror64(B0, 63); \ + B1 = ror64(B1, 63); \ + } while ((void)0, 0) + +#define DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + B0 = _mm512_permutex_epi64(B0, _MM_SHUFFLE(0, 3, 2, 1)); \ + B1 = _mm512_permutex_epi64(B1, _MM_SHUFFLE(0, 3, 2, 1)); \ +\ + C0 = _mm512_permutex_epi64(C0, _MM_SHUFFLE(1, 0, 3, 2)); \ + C1 = _mm512_permutex_epi64(C1, _MM_SHUFFLE(1, 0, 3, 2)); \ +\ + D0 = _mm512_permutex_epi64(D0, _MM_SHUFFLE(2, 1, 0, 3)); \ + D1 = _mm512_permutex_epi64(D1, _MM_SHUFFLE(2, 1, 0, 3)); \ + } while ((void)0, 0) + +#define UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + B0 = _mm512_permutex_epi64(B0, _MM_SHUFFLE(2, 1, 0, 3)); \ + B1 = _mm512_permutex_epi64(B1, _MM_SHUFFLE(2, 1, 0, 3)); \ +\ + C0 = _mm512_permutex_epi64(C0, _MM_SHUFFLE(1, 0, 3, 2)); \ + C1 = _mm512_permutex_epi64(C1, _MM_SHUFFLE(1, 0, 3, 2)); \ +\ + D0 = _mm512_permutex_epi64(D0, _MM_SHUFFLE(0, 3, 2, 1)); \ + D1 = _mm512_permutex_epi64(D1, _MM_SHUFFLE(0, 3, 2, 1)); \ + } while ((void)0, 0) + +#define BLAKE2_ROUND(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + G1(A0, B0, C0, D0, A1, B1, C1, D1); \ + G2(A0, B0, C0, D0, A1, B1, C1, D1); \ +\ + DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1); \ +\ + G1(A0, B0, C0, D0, A1, B1, C1, D1); \ + G2(A0, B0, C0, D0, A1, B1, C1, D1); \ +\ + UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1); \ + } while ((void)0, 0) + +#define SWAP_HALVES(A0, A1) \ + do { \ + __m512i t0, t1; \ + t0 = _mm512_shuffle_i64x2(A0, A1, _MM_SHUFFLE(1, 0, 1, 0)); \ + t1 = _mm512_shuffle_i64x2(A0, A1, _MM_SHUFFLE(3, 2, 3, 2)); \ + A0 = t0; \ + A1 = t1; \ + } while((void)0, 0) + +#define SWAP_QUARTERS(A0, A1) \ + do { \ + SWAP_HALVES(A0, A1); \ + A0 = _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 1, 4, 5, 2, 3, 6, 7), A0); \ + A1 = _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 1, 4, 5, 2, 3, 6, 7), A1); \ + } while((void)0, 0) + +#define UNSWAP_QUARTERS(A0, A1) \ + do { \ + A0 = _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 1, 4, 5, 2, 3, 6, 7), A0); \ + A1 = _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 1, 4, 5, 2, 3, 6, 7), A1); \ + SWAP_HALVES(A0, A1); \ + } while((void)0, 0) + +#define BLAKE2_ROUND_1(A0, C0, B0, D0, A1, C1, B1, D1) \ + do { \ + SWAP_HALVES(A0, B0); \ + SWAP_HALVES(C0, D0); \ + SWAP_HALVES(A1, B1); \ + SWAP_HALVES(C1, D1); \ + BLAKE2_ROUND(A0, B0, C0, D0, A1, B1, C1, D1); \ + SWAP_HALVES(A0, B0); \ + SWAP_HALVES(C0, D0); \ + SWAP_HALVES(A1, B1); \ + SWAP_HALVES(C1, D1); \ + } while ((void)0, 0) + +#define BLAKE2_ROUND_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + SWAP_QUARTERS(A0, A1); \ + SWAP_QUARTERS(B0, B1); \ + SWAP_QUARTERS(C0, C1); \ + SWAP_QUARTERS(D0, D1); \ + BLAKE2_ROUND(A0, B0, C0, D0, A1, B1, C1, D1); \ + UNSWAP_QUARTERS(A0, A1); \ + UNSWAP_QUARTERS(B0, B1); \ + UNSWAP_QUARTERS(C0, C1); \ + UNSWAP_QUARTERS(D0, D1); \ + } while ((void)0, 0) + +#endif /* __AVX512F__ */ +#endif /* BLAKE_ROUND_MKA_OPT_H */ diff --git a/framework/3rd/argon2/src/blake2/blamka-round-ref.h b/framework/3rd/argon2/src/blake2/blamka-round-ref.h new file mode 100755 index 0000000..16cfc1c --- /dev/null +++ b/framework/3rd/argon2/src/blake2/blamka-round-ref.h @@ -0,0 +1,56 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef BLAKE_ROUND_MKA_H +#define BLAKE_ROUND_MKA_H + +#include "blake2.h" +#include "blake2-impl.h" + +/* designed by the Lyra PHC team */ +static BLAKE2_INLINE uint64_t fBlaMka(uint64_t x, uint64_t y) { + const uint64_t m = UINT64_C(0xFFFFFFFF); + const uint64_t xy = (x & m) * (y & m); + return x + y + 2 * xy; +} + +#define G(a, b, c, d) \ + do { \ + a = fBlaMka(a, b); \ + d = rotr64(d ^ a, 32); \ + c = fBlaMka(c, d); \ + b = rotr64(b ^ c, 24); \ + a = fBlaMka(a, b); \ + d = rotr64(d ^ a, 16); \ + c = fBlaMka(c, d); \ + b = rotr64(b ^ c, 63); \ + } while ((void)0, 0) + +#define BLAKE2_ROUND_NOMSG(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, \ + v12, v13, v14, v15) \ + do { \ + G(v0, v4, v8, v12); \ + G(v1, v5, v9, v13); \ + G(v2, v6, v10, v14); \ + G(v3, v7, v11, v15); \ + G(v0, v5, v10, v15); \ + G(v1, v6, v11, v12); \ + G(v2, v7, v8, v13); \ + G(v3, v4, v9, v14); \ + } while ((void)0, 0) + +#endif diff --git a/framework/3rd/argon2/src/core.c b/framework/3rd/argon2/src/core.c new file mode 100755 index 0000000..e697882 --- /dev/null +++ b/framework/3rd/argon2/src/core.c @@ -0,0 +1,648 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +/*For memory wiping*/ +#ifdef _WIN32 +#include +#include /* For SecureZeroMemory */ +#endif +#if defined __STDC_LIB_EXT1__ +#define __STDC_WANT_LIB_EXT1__ 1 +#endif +#define VC_GE_2005(version) (version >= 1400) + +/* for explicit_bzero() on glibc */ +#define _DEFAULT_SOURCE + +#include +#include +#include + +#include "core.h" +#include "thread.h" +#include "blake2/blake2.h" +#include "blake2/blake2-impl.h" + +#ifdef GENKAT +#include "genkat.h" +#endif + +#if defined(__clang__) +#if __has_attribute(optnone) +#define NOT_OPTIMIZED __attribute__((optnone)) +#endif +#elif defined(__GNUC__) +#define GCC_VERSION \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#if GCC_VERSION >= 40400 +#define NOT_OPTIMIZED __attribute__((optimize("O0"))) +#endif +#endif +#ifndef NOT_OPTIMIZED +#define NOT_OPTIMIZED +#endif + +/***************Instance and Position constructors**********/ +void init_block_value(block *b, uint8_t in) { memset(b->v, in, sizeof(b->v)); } + +void copy_block(block *dst, const block *src) { + memcpy(dst->v, src->v, sizeof(uint64_t) * ARGON2_QWORDS_IN_BLOCK); +} + +void xor_block(block *dst, const block *src) { + int i; + for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { + dst->v[i] ^= src->v[i]; + } +} + +static void load_block(block *dst, const void *input) { + unsigned i; + for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { + dst->v[i] = load64((const uint8_t *)input + i * sizeof(dst->v[i])); + } +} + +static void store_block(void *output, const block *src) { + unsigned i; + for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { + store64((uint8_t *)output + i * sizeof(src->v[i]), src->v[i]); + } +} + +/***************Memory functions*****************/ + +int allocate_memory(const argon2_context *context, uint8_t **memory, + size_t num, size_t size) { + size_t memory_size = num*size; + if (memory == NULL) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + /* 1. Check for multiplication overflow */ + if (size != 0 && memory_size / size != num) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + /* 2. Try to allocate with appropriate allocator */ + if (context->allocate_cbk) { + (context->allocate_cbk)(memory, memory_size); + } else { + *memory = malloc(memory_size); + } + + if (*memory == NULL) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + return ARGON2_OK; +} + +void free_memory(const argon2_context *context, uint8_t *memory, + size_t num, size_t size) { + size_t memory_size = num*size; + clear_internal_memory(memory, memory_size); + if (context->free_cbk) { + (context->free_cbk)(memory, memory_size); + } else { + free(memory); + } +} + +#if defined(__OpenBSD__) +#define HAVE_EXPLICIT_BZERO 1 +#elif defined(__GLIBC__) && defined(__GLIBC_PREREQ) +#if __GLIBC_PREREQ(2,25) +#define HAVE_EXPLICIT_BZERO 1 +#endif +#endif + +void NOT_OPTIMIZED secure_wipe_memory(void *v, size_t n) { +#if defined(_MSC_VER) && VC_GE_2005(_MSC_VER) || defined(__MINGW32__) + SecureZeroMemory(v, n); +#elif defined memset_s + memset_s(v, n, 0, n); +#elif defined(HAVE_EXPLICIT_BZERO) + explicit_bzero(v, n); +#else + static void *(*const volatile memset_sec)(void *, int, size_t) = &memset; + memset_sec(v, 0, n); +#endif +} + +/* Memory clear flag defaults to true. */ +int FLAG_clear_internal_memory = 1; +void clear_internal_memory(void *v, size_t n) { + if (FLAG_clear_internal_memory && v) { + secure_wipe_memory(v, n); + } +} + +void finalize(const argon2_context *context, argon2_instance_t *instance) { + if (context != NULL && instance != NULL) { + block blockhash; + uint32_t l; + + copy_block(&blockhash, instance->memory + instance->lane_length - 1); + + /* XOR the last blocks */ + for (l = 1; l < instance->lanes; ++l) { + uint32_t last_block_in_lane = + l * instance->lane_length + (instance->lane_length - 1); + xor_block(&blockhash, instance->memory + last_block_in_lane); + } + + /* Hash the result */ + { + uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE]; + store_block(blockhash_bytes, &blockhash); + blake2b_long(context->out, context->outlen, blockhash_bytes, + ARGON2_BLOCK_SIZE); + /* clear blockhash and blockhash_bytes */ + clear_internal_memory(blockhash.v, ARGON2_BLOCK_SIZE); + clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE); + } + +#ifdef GENKAT + print_tag(context->out, context->outlen); +#endif + + free_memory(context, (uint8_t *)instance->memory, + instance->memory_blocks, sizeof(block)); + } +} + +uint32_t index_alpha(const argon2_instance_t *instance, + const argon2_position_t *position, uint32_t pseudo_rand, + int same_lane) { + /* + * Pass 0: + * This lane : all already finished segments plus already constructed + * blocks in this segment + * Other lanes : all already finished segments + * Pass 1+: + * This lane : (SYNC_POINTS - 1) last segments plus already constructed + * blocks in this segment + * Other lanes : (SYNC_POINTS - 1) last segments + */ + uint32_t reference_area_size; + uint64_t relative_position; + uint32_t start_position, absolute_position; + + if (0 == position->pass) { + /* First pass */ + if (0 == position->slice) { + /* First slice */ + reference_area_size = + position->index - 1; /* all but the previous */ + } else { + if (same_lane) { + /* The same lane => add current segment */ + reference_area_size = + position->slice * instance->segment_length + + position->index - 1; + } else { + reference_area_size = + position->slice * instance->segment_length + + ((position->index == 0) ? (-1) : 0); + } + } + } else { + /* Second pass */ + if (same_lane) { + reference_area_size = instance->lane_length - + instance->segment_length + position->index - + 1; + } else { + reference_area_size = instance->lane_length - + instance->segment_length + + ((position->index == 0) ? (-1) : 0); + } + } + + /* 1.2.4. Mapping pseudo_rand to 0.. and produce + * relative position */ + relative_position = pseudo_rand; + relative_position = relative_position * relative_position >> 32; + relative_position = reference_area_size - 1 - + (reference_area_size * relative_position >> 32); + + /* 1.2.5 Computing starting position */ + start_position = 0; + + if (0 != position->pass) { + start_position = (position->slice == ARGON2_SYNC_POINTS - 1) + ? 0 + : (position->slice + 1) * instance->segment_length; + } + + /* 1.2.6. Computing absolute position */ + absolute_position = (start_position + relative_position) % + instance->lane_length; /* absolute position */ + return absolute_position; +} + +/* Single-threaded version for p=1 case */ +static int fill_memory_blocks_st(argon2_instance_t *instance) { + uint32_t r, s, l; + + for (r = 0; r < instance->passes; ++r) { + for (s = 0; s < ARGON2_SYNC_POINTS; ++s) { + for (l = 0; l < instance->lanes; ++l) { + argon2_position_t position = {r, l, (uint8_t)s, 0}; + fill_segment(instance, position); + } + } +#ifdef GENKAT + internal_kat(instance, r); /* Print all memory blocks */ +#endif + } + return ARGON2_OK; +} + +#if !defined(ARGON2_NO_THREADS) + +#ifdef _WIN32 +static unsigned __stdcall fill_segment_thr(void *thread_data) +#else +static void *fill_segment_thr(void *thread_data) +#endif +{ + argon2_thread_data *my_data = thread_data; + fill_segment(my_data->instance_ptr, my_data->pos); + argon2_thread_exit(); + return 0; +} + +/* Multi-threaded version for p > 1 case */ +static int fill_memory_blocks_mt(argon2_instance_t *instance) { + uint32_t r, s; + argon2_thread_handle_t *thread = NULL; + argon2_thread_data *thr_data = NULL; + int rc = ARGON2_OK; + + /* 1. Allocating space for threads */ + thread = calloc(instance->lanes, sizeof(argon2_thread_handle_t)); + if (thread == NULL) { + rc = ARGON2_MEMORY_ALLOCATION_ERROR; + goto fail; + } + + thr_data = calloc(instance->lanes, sizeof(argon2_thread_data)); + if (thr_data == NULL) { + rc = ARGON2_MEMORY_ALLOCATION_ERROR; + goto fail; + } + + for (r = 0; r < instance->passes; ++r) { + for (s = 0; s < ARGON2_SYNC_POINTS; ++s) { + uint32_t l, ll; + + /* 2. Calling threads */ + for (l = 0; l < instance->lanes; ++l) { + argon2_position_t position; + + /* 2.1 Join a thread if limit is exceeded */ + if (l >= instance->threads) { + if (argon2_thread_join(thread[l - instance->threads])) { + rc = ARGON2_THREAD_FAIL; + goto fail; + } + } + + /* 2.2 Create thread */ + position.pass = r; + position.lane = l; + position.slice = (uint8_t)s; + position.index = 0; + thr_data[l].instance_ptr = + instance; /* preparing the thread input */ + memcpy(&(thr_data[l].pos), &position, + sizeof(argon2_position_t)); + if (argon2_thread_create(&thread[l], &fill_segment_thr, + (void *)&thr_data[l])) { + /* Wait for already running threads */ + for (ll = 0; ll < l; ++ll) + argon2_thread_join(thread[ll]); + rc = ARGON2_THREAD_FAIL; + goto fail; + } + + /* fill_segment(instance, position); */ + /*Non-thread equivalent of the lines above */ + } + + /* 3. Joining remaining threads */ + for (l = instance->lanes - instance->threads; l < instance->lanes; + ++l) { + if (argon2_thread_join(thread[l])) { + rc = ARGON2_THREAD_FAIL; + goto fail; + } + } + } + +#ifdef GENKAT + internal_kat(instance, r); /* Print all memory blocks */ +#endif + } + +fail: + if (thread != NULL) { + free(thread); + } + if (thr_data != NULL) { + free(thr_data); + } + return rc; +} + +#endif /* ARGON2_NO_THREADS */ + +int fill_memory_blocks(argon2_instance_t *instance) { + if (instance == NULL || instance->lanes == 0) { + return ARGON2_INCORRECT_PARAMETER; + } +#if defined(ARGON2_NO_THREADS) + return fill_memory_blocks_st(instance); +#else + return instance->threads == 1 ? + fill_memory_blocks_st(instance) : fill_memory_blocks_mt(instance); +#endif +} + +int validate_inputs(const argon2_context *context) { + if (NULL == context) { + return ARGON2_INCORRECT_PARAMETER; + } + + if (NULL == context->out) { + return ARGON2_OUTPUT_PTR_NULL; + } + + /* Validate output length */ + if (ARGON2_MIN_OUTLEN > context->outlen) { + return ARGON2_OUTPUT_TOO_SHORT; + } + + if (ARGON2_MAX_OUTLEN < context->outlen) { + return ARGON2_OUTPUT_TOO_LONG; + } + + /* Validate password (required param) */ + if (NULL == context->pwd) { + if (0 != context->pwdlen) { + return ARGON2_PWD_PTR_MISMATCH; + } + } + + if (ARGON2_MIN_PWD_LENGTH > context->pwdlen) { + return ARGON2_PWD_TOO_SHORT; + } + + if (ARGON2_MAX_PWD_LENGTH < context->pwdlen) { + return ARGON2_PWD_TOO_LONG; + } + + /* Validate salt (required param) */ + if (NULL == context->salt) { + if (0 != context->saltlen) { + return ARGON2_SALT_PTR_MISMATCH; + } + } + + if (ARGON2_MIN_SALT_LENGTH > context->saltlen) { + return ARGON2_SALT_TOO_SHORT; + } + + if (ARGON2_MAX_SALT_LENGTH < context->saltlen) { + return ARGON2_SALT_TOO_LONG; + } + + /* Validate secret (optional param) */ + if (NULL == context->secret) { + if (0 != context->secretlen) { + return ARGON2_SECRET_PTR_MISMATCH; + } + } else { + if (ARGON2_MIN_SECRET > context->secretlen) { + return ARGON2_SECRET_TOO_SHORT; + } + if (ARGON2_MAX_SECRET < context->secretlen) { + return ARGON2_SECRET_TOO_LONG; + } + } + + /* Validate associated data (optional param) */ + if (NULL == context->ad) { + if (0 != context->adlen) { + return ARGON2_AD_PTR_MISMATCH; + } + } else { + if (ARGON2_MIN_AD_LENGTH > context->adlen) { + return ARGON2_AD_TOO_SHORT; + } + if (ARGON2_MAX_AD_LENGTH < context->adlen) { + return ARGON2_AD_TOO_LONG; + } + } + + /* Validate memory cost */ + if (ARGON2_MIN_MEMORY > context->m_cost) { + return ARGON2_MEMORY_TOO_LITTLE; + } + + if (ARGON2_MAX_MEMORY < context->m_cost) { + return ARGON2_MEMORY_TOO_MUCH; + } + + if (context->m_cost < 8 * context->lanes) { + return ARGON2_MEMORY_TOO_LITTLE; + } + + /* Validate time cost */ + if (ARGON2_MIN_TIME > context->t_cost) { + return ARGON2_TIME_TOO_SMALL; + } + + if (ARGON2_MAX_TIME < context->t_cost) { + return ARGON2_TIME_TOO_LARGE; + } + + /* Validate lanes */ + if (ARGON2_MIN_LANES > context->lanes) { + return ARGON2_LANES_TOO_FEW; + } + + if (ARGON2_MAX_LANES < context->lanes) { + return ARGON2_LANES_TOO_MANY; + } + + /* Validate threads */ + if (ARGON2_MIN_THREADS > context->threads) { + return ARGON2_THREADS_TOO_FEW; + } + + if (ARGON2_MAX_THREADS < context->threads) { + return ARGON2_THREADS_TOO_MANY; + } + + if (NULL != context->allocate_cbk && NULL == context->free_cbk) { + return ARGON2_FREE_MEMORY_CBK_NULL; + } + + if (NULL == context->allocate_cbk && NULL != context->free_cbk) { + return ARGON2_ALLOCATE_MEMORY_CBK_NULL; + } + + return ARGON2_OK; +} + +void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance) { + uint32_t l; + /* Make the first and second block in each lane as G(H0||0||i) or + G(H0||1||i) */ + uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE]; + for (l = 0; l < instance->lanes; ++l) { + + store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 0); + store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH + 4, l); + blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash, + ARGON2_PREHASH_SEED_LENGTH); + load_block(&instance->memory[l * instance->lane_length + 0], + blockhash_bytes); + + store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 1); + blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash, + ARGON2_PREHASH_SEED_LENGTH); + load_block(&instance->memory[l * instance->lane_length + 1], + blockhash_bytes); + } + clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE); +} + +void initial_hash(uint8_t *blockhash, argon2_context *context, + argon2_type type) { + blake2b_state BlakeHash; + uint8_t value[sizeof(uint32_t)]; + + if (NULL == context || NULL == blockhash) { + return; + } + + blake2b_init(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH); + + store32(&value, context->lanes); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->outlen); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->m_cost); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->t_cost); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->version); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, (uint32_t)type); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->pwdlen); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + if (context->pwd != NULL) { + blake2b_update(&BlakeHash, (const uint8_t *)context->pwd, + context->pwdlen); + + if (context->flags & ARGON2_FLAG_CLEAR_PASSWORD) { + secure_wipe_memory(context->pwd, context->pwdlen); + context->pwdlen = 0; + } + } + + store32(&value, context->saltlen); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + if (context->salt != NULL) { + blake2b_update(&BlakeHash, (const uint8_t *)context->salt, + context->saltlen); + } + + store32(&value, context->secretlen); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + if (context->secret != NULL) { + blake2b_update(&BlakeHash, (const uint8_t *)context->secret, + context->secretlen); + + if (context->flags & ARGON2_FLAG_CLEAR_SECRET) { + secure_wipe_memory(context->secret, context->secretlen); + context->secretlen = 0; + } + } + + store32(&value, context->adlen); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + if (context->ad != NULL) { + blake2b_update(&BlakeHash, (const uint8_t *)context->ad, + context->adlen); + } + + blake2b_final(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH); +} + +int initialize(argon2_instance_t *instance, argon2_context *context) { + uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; + int result = ARGON2_OK; + + if (instance == NULL || context == NULL) + return ARGON2_INCORRECT_PARAMETER; + instance->context_ptr = context; + + /* 1. Memory allocation */ + result = allocate_memory(context, (uint8_t **)&(instance->memory), + instance->memory_blocks, sizeof(block)); + if (result != ARGON2_OK) { + return result; + } + + /* 2. Initial hashing */ + /* H_0 + 8 extra bytes to produce the first blocks */ + /* uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; */ + /* Hashing all inputs */ + initial_hash(blockhash, context, instance->type); + /* Zeroing 8 extra bytes */ + clear_internal_memory(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, + ARGON2_PREHASH_SEED_LENGTH - + ARGON2_PREHASH_DIGEST_LENGTH); + +#ifdef GENKAT + initial_kat(blockhash, context, instance->type); +#endif + + /* 3. Creating first blocks, we always have at least two blocks in a slice + */ + fill_first_blocks(blockhash, instance); + /* Clearing the hash */ + clear_internal_memory(blockhash, ARGON2_PREHASH_SEED_LENGTH); + + return ARGON2_OK; +} diff --git a/framework/3rd/argon2/src/core.h b/framework/3rd/argon2/src/core.h new file mode 100755 index 0000000..59e2564 --- /dev/null +++ b/framework/3rd/argon2/src/core.h @@ -0,0 +1,228 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ARGON2_CORE_H +#define ARGON2_CORE_H + +#include "argon2.h" + +#define CONST_CAST(x) (x)(uintptr_t) + +/**********************Argon2 internal constants*******************************/ + +enum argon2_core_constants { + /* Memory block size in bytes */ + ARGON2_BLOCK_SIZE = 1024, + ARGON2_QWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 8, + ARGON2_OWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 16, + ARGON2_HWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 32, + ARGON2_512BIT_WORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 64, + + /* Number of pseudo-random values generated by one call to Blake in Argon2i + to + generate reference block positions */ + ARGON2_ADDRESSES_IN_BLOCK = 128, + + /* Pre-hashing digest length and its extension*/ + ARGON2_PREHASH_DIGEST_LENGTH = 64, + ARGON2_PREHASH_SEED_LENGTH = 72 +}; + +/*************************Argon2 internal data types***********************/ + +/* + * Structure for the (1KB) memory block implemented as 128 64-bit words. + * Memory blocks can be copied, XORed. Internal words can be accessed by [] (no + * bounds checking). + */ +typedef struct block_ { uint64_t v[ARGON2_QWORDS_IN_BLOCK]; } block; + +/*****************Functions that work with the block******************/ + +/* Initialize each byte of the block with @in */ +void init_block_value(block *b, uint8_t in); + +/* Copy block @src to block @dst */ +void copy_block(block *dst, const block *src); + +/* XOR @src onto @dst bytewise */ +void xor_block(block *dst, const block *src); + +/* + * Argon2 instance: memory pointer, number of passes, amount of memory, type, + * and derived values. + * Used to evaluate the number and location of blocks to construct in each + * thread + */ +typedef struct Argon2_instance_t { + block *memory; /* Memory pointer */ + uint32_t version; + uint32_t passes; /* Number of passes */ + uint32_t memory_blocks; /* Number of blocks in memory */ + uint32_t segment_length; + uint32_t lane_length; + uint32_t lanes; + uint32_t threads; + argon2_type type; + int print_internals; /* whether to print the memory blocks */ + argon2_context *context_ptr; /* points back to original context */ +} argon2_instance_t; + +/* + * Argon2 position: where we construct the block right now. Used to distribute + * work between threads. + */ +typedef struct Argon2_position_t { + uint32_t pass; + uint32_t lane; + uint8_t slice; + uint32_t index; +} argon2_position_t; + +/*Struct that holds the inputs for thread handling FillSegment*/ +typedef struct Argon2_thread_data { + argon2_instance_t *instance_ptr; + argon2_position_t pos; +} argon2_thread_data; + +/*************************Argon2 core functions********************************/ + +/* Allocates memory to the given pointer, uses the appropriate allocator as + * specified in the context. Total allocated memory is num*size. + * @param context argon2_context which specifies the allocator + * @param memory pointer to the pointer to the memory + * @param size the size in bytes for each element to be allocated + * @param num the number of elements to be allocated + * @return ARGON2_OK if @memory is a valid pointer and memory is allocated + */ +int allocate_memory(const argon2_context *context, uint8_t **memory, + size_t num, size_t size); + +/* + * Frees memory at the given pointer, uses the appropriate deallocator as + * specified in the context. Also cleans the memory using clear_internal_memory. + * @param context argon2_context which specifies the deallocator + * @param memory pointer to buffer to be freed + * @param size the size in bytes for each element to be deallocated + * @param num the number of elements to be deallocated + */ +void free_memory(const argon2_context *context, uint8_t *memory, + size_t num, size_t size); + +/* Function that securely cleans the memory. This ignores any flags set + * regarding clearing memory. Usually one just calls clear_internal_memory. + * @param mem Pointer to the memory + * @param s Memory size in bytes + */ +void secure_wipe_memory(void *v, size_t n); + +/* Function that securely clears the memory if FLAG_clear_internal_memory is + * set. If the flag isn't set, this function does nothing. + * @param mem Pointer to the memory + * @param s Memory size in bytes + */ +void clear_internal_memory(void *v, size_t n); + +/* + * Computes absolute position of reference block in the lane following a skewed + * distribution and using a pseudo-random value as input + * @param instance Pointer to the current instance + * @param position Pointer to the current position + * @param pseudo_rand 32-bit pseudo-random value used to determine the position + * @param same_lane Indicates if the block will be taken from the current lane. + * If so we can reference the current segment + * @pre All pointers must be valid + */ +uint32_t index_alpha(const argon2_instance_t *instance, + const argon2_position_t *position, uint32_t pseudo_rand, + int same_lane); + +/* + * Function that validates all inputs against predefined restrictions and return + * an error code + * @param context Pointer to current Argon2 context + * @return ARGON2_OK if everything is all right, otherwise one of error codes + * (all defined in + */ +int validate_inputs(const argon2_context *context); + +/* + * Hashes all the inputs into @a blockhash[PREHASH_DIGEST_LENGTH], clears + * password and secret if needed + * @param context Pointer to the Argon2 internal structure containing memory + * pointer, and parameters for time and space requirements. + * @param blockhash Buffer for pre-hashing digest + * @param type Argon2 type + * @pre @a blockhash must have at least @a PREHASH_DIGEST_LENGTH bytes + * allocated + */ +void initial_hash(uint8_t *blockhash, argon2_context *context, + argon2_type type); + +/* + * Function creates first 2 blocks per lane + * @param instance Pointer to the current instance + * @param blockhash Pointer to the pre-hashing digest + * @pre blockhash must point to @a PREHASH_SEED_LENGTH allocated values + */ +void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance); + +/* + * Function allocates memory, hashes the inputs with Blake, and creates first + * two blocks. Returns the pointer to the main memory with 2 blocks per lane + * initialized + * @param context Pointer to the Argon2 internal structure containing memory + * pointer, and parameters for time and space requirements. + * @param instance Current Argon2 instance + * @return Zero if successful, -1 if memory failed to allocate. @context->state + * will be modified if successful. + */ +int initialize(argon2_instance_t *instance, argon2_context *context); + +/* + * XORing the last block of each lane, hashing it, making the tag. Deallocates + * the memory. + * @param context Pointer to current Argon2 context (use only the out parameters + * from it) + * @param instance Pointer to current instance of Argon2 + * @pre instance->state must point to necessary amount of memory + * @pre context->out must point to outlen bytes of memory + * @pre if context->free_cbk is not NULL, it should point to a function that + * deallocates memory + */ +void finalize(const argon2_context *context, argon2_instance_t *instance); + +/* + * Function that fills the segment using previous segments also from other + * threads + * @param context current context + * @param instance Pointer to the current instance + * @param position Current position + * @pre all block pointers must be valid + */ +void fill_segment(const argon2_instance_t *instance, + argon2_position_t position); + +/* + * Function that fills the entire memory t_cost times based on the first two + * blocks in each lane + * @param instance Pointer to the current instance + * @return ARGON2_OK if successful, @context->state + */ +int fill_memory_blocks(argon2_instance_t *instance); + +#endif diff --git a/framework/3rd/argon2/src/encoding.c b/framework/3rd/argon2/src/encoding.c new file mode 100755 index 0000000..771a440 --- /dev/null +++ b/framework/3rd/argon2/src/encoding.c @@ -0,0 +1,463 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include +#include +#include "encoding.h" +#include "core.h" + +/* + * Example code for a decoder and encoder of "hash strings", with Argon2 + * parameters. + * + * This code comprises three sections: + * + * -- The first section contains generic Base64 encoding and decoding + * functions. It is conceptually applicable to any hash function + * implementation that uses Base64 to encode and decode parameters, + * salts and outputs. It could be made into a library, provided that + * the relevant functions are made public (non-static) and be given + * reasonable names to avoid collisions with other functions. + * + * -- The second section is specific to Argon2. It encodes and decodes + * the parameters, salts and outputs. It does not compute the hash + * itself. + * + * The code was originally written by Thomas Pornin , + * to whom comments and remarks may be sent. It is released under what + * should amount to Public Domain or its closest equivalent; the + * following mantra is supposed to incarnate that fact with all the + * proper legal rituals: + * + * --------------------------------------------------------------------- + * This file is provided under the terms of Creative Commons CC0 1.0 + * Public Domain Dedication. To the extent possible under law, the + * author (Thomas Pornin) has waived all copyright and related or + * neighboring rights to this file. This work is published from: Canada. + * --------------------------------------------------------------------- + * + * Copyright (c) 2015 Thomas Pornin + */ + +/* ==================================================================== */ +/* + * Common code; could be shared between different hash functions. + * + * Note: the Base64 functions below assume that uppercase letters (resp. + * lowercase letters) have consecutive numerical codes, that fit on 8 + * bits. All modern systems use ASCII-compatible charsets, where these + * properties are true. If you are stuck with a dinosaur of a system + * that still defaults to EBCDIC then you already have much bigger + * interoperability issues to deal with. + */ + +/* + * Some macros for constant-time comparisons. These work over values in + * the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true". + */ +#define EQ(x, y) ((((0U - ((unsigned)(x) ^ (unsigned)(y))) >> 8) & 0xFF) ^ 0xFF) +#define GT(x, y) ((((unsigned)(y) - (unsigned)(x)) >> 8) & 0xFF) +#define GE(x, y) (GT(y, x) ^ 0xFF) +#define LT(x, y) GT(y, x) +#define LE(x, y) GE(y, x) + +/* + * Convert value x (0..63) to corresponding Base64 character. + */ +static int b64_byte_to_char(unsigned x) { + return (LT(x, 26) & (x + 'A')) | + (GE(x, 26) & LT(x, 52) & (x + ('a' - 26))) | + (GE(x, 52) & LT(x, 62) & (x + ('0' - 52))) | (EQ(x, 62) & '+') | + (EQ(x, 63) & '/'); +} + +/* + * Convert character c to the corresponding 6-bit value. If character c + * is not a Base64 character, then 0xFF (255) is returned. + */ +static unsigned b64_char_to_byte(int c) { + unsigned x; + + x = (GE(c, 'A') & LE(c, 'Z') & (c - 'A')) | + (GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) | + (GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '+') & 62) | + (EQ(c, '/') & 63); + return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF)); +} + +/* + * Convert some bytes to Base64. 'dst_len' is the length (in characters) + * of the output buffer 'dst'; if that buffer is not large enough to + * receive the result (including the terminating 0), then (size_t)-1 + * is returned. Otherwise, the zero-terminated Base64 string is written + * in the buffer, and the output length (counted WITHOUT the terminating + * zero) is returned. + */ +static size_t to_base64(char *dst, size_t dst_len, const void *src, + size_t src_len) { + size_t olen; + const unsigned char *buf; + unsigned acc, acc_len; + + olen = (src_len / 3) << 2; + switch (src_len % 3) { + case 2: + olen++; + /* fall through */ + case 1: + olen += 2; + break; + } + if (dst_len <= olen) { + return (size_t)-1; + } + acc = 0; + acc_len = 0; + buf = (const unsigned char *)src; + while (src_len-- > 0) { + acc = (acc << 8) + (*buf++); + acc_len += 8; + while (acc_len >= 6) { + acc_len -= 6; + *dst++ = (char)b64_byte_to_char((acc >> acc_len) & 0x3F); + } + } + if (acc_len > 0) { + *dst++ = (char)b64_byte_to_char((acc << (6 - acc_len)) & 0x3F); + } + *dst++ = 0; + return olen; +} + +/* + * Decode Base64 chars into bytes. The '*dst_len' value must initially + * contain the length of the output buffer '*dst'; when the decoding + * ends, the actual number of decoded bytes is written back in + * '*dst_len'. + * + * Decoding stops when a non-Base64 character is encountered, or when + * the output buffer capacity is exceeded. If an error occurred (output + * buffer is too small, invalid last characters leading to unprocessed + * buffered bits), then NULL is returned; otherwise, the returned value + * points to the first non-Base64 character in the source stream, which + * may be the terminating zero. + */ +static const char *from_base64(void *dst, size_t *dst_len, const char *src) { + size_t len; + unsigned char *buf; + unsigned acc, acc_len; + + buf = (unsigned char *)dst; + len = 0; + acc = 0; + acc_len = 0; + for (;;) { + unsigned d; + + d = b64_char_to_byte(*src); + if (d == 0xFF) { + break; + } + src++; + acc = (acc << 6) + d; + acc_len += 6; + if (acc_len >= 8) { + acc_len -= 8; + if ((len++) >= *dst_len) { + return NULL; + } + *buf++ = (acc >> acc_len) & 0xFF; + } + } + + /* + * If the input length is equal to 1 modulo 4 (which is + * invalid), then there will remain 6 unprocessed bits; + * otherwise, only 0, 2 or 4 bits are buffered. The buffered + * bits must also all be zero. + */ + if (acc_len > 4 || (acc & (((unsigned)1 << acc_len) - 1)) != 0) { + return NULL; + } + *dst_len = len; + return src; +} + +/* + * Decode decimal integer from 'str'; the value is written in '*v'. + * Returned value is a pointer to the next non-decimal character in the + * string. If there is no digit at all, or the value encoding is not + * minimal (extra leading zeros), or the value does not fit in an + * 'unsigned long', then NULL is returned. + */ +static const char *decode_decimal(const char *str, unsigned long *v) { + const char *orig; + unsigned long acc; + + acc = 0; + for (orig = str;; str++) { + int c; + + c = *str; + if (c < '0' || c > '9') { + break; + } + c -= '0'; + if (acc > (ULONG_MAX / 10)) { + return NULL; + } + acc *= 10; + if ((unsigned long)c > (ULONG_MAX - acc)) { + return NULL; + } + acc += (unsigned long)c; + } + if (str == orig || (*orig == '0' && str != (orig + 1))) { + return NULL; + } + *v = acc; + return str; +} + +/* ==================================================================== */ +/* + * Code specific to Argon2. + * + * The code below applies the following format: + * + * $argon2[$v=]$m=,t=,p=$$ + * + * where is either 'd', 'id', or 'i', is a decimal integer (positive, + * fits in an 'unsigned long'), and is Base64-encoded data (no '=' padding + * characters, no newline or whitespace). + * + * The last two binary chunks (encoded in Base64) are, in that order, + * the salt and the output. Both are required. The binary salt length and the + * output length must be in the allowed ranges defined in argon2.h. + * + * The ctx struct must contain buffers large enough to hold the salt and pwd + * when it is fed into decode_string. + */ + +int decode_string(argon2_context *ctx, const char *str, argon2_type type) { + +/* check for prefix */ +#define CC(prefix) \ + do { \ + size_t cc_len = strlen(prefix); \ + if (strncmp(str, prefix, cc_len) != 0) { \ + return ARGON2_DECODING_FAIL; \ + } \ + str += cc_len; \ + } while ((void)0, 0) + +/* optional prefix checking with supplied code */ +#define CC_opt(prefix, code) \ + do { \ + size_t cc_len = strlen(prefix); \ + if (strncmp(str, prefix, cc_len) == 0) { \ + str += cc_len; \ + { code; } \ + } \ + } while ((void)0, 0) + +/* Decoding prefix into decimal */ +#define DECIMAL(x) \ + do { \ + unsigned long dec_x; \ + str = decode_decimal(str, &dec_x); \ + if (str == NULL) { \ + return ARGON2_DECODING_FAIL; \ + } \ + (x) = dec_x; \ + } while ((void)0, 0) + + +/* Decoding prefix into uint32_t decimal */ +#define DECIMAL_U32(x) \ + do { \ + unsigned long dec_x; \ + str = decode_decimal(str, &dec_x); \ + if (str == NULL || dec_x > UINT32_MAX) { \ + return ARGON2_DECODING_FAIL; \ + } \ + (x) = (uint32_t)dec_x; \ + } while ((void)0, 0) + + +/* Decoding base64 into a binary buffer */ +#define BIN(buf, max_len, len) \ + do { \ + size_t bin_len = (max_len); \ + str = from_base64(buf, &bin_len, str); \ + if (str == NULL || bin_len > UINT32_MAX) { \ + return ARGON2_DECODING_FAIL; \ + } \ + (len) = (uint32_t)bin_len; \ + } while ((void)0, 0) + + size_t maxsaltlen = ctx->saltlen; + size_t maxoutlen = ctx->outlen; + int validation_result; + const char* type_string; + + /* We should start with the argon2_type we are using */ + type_string = argon2_type2string(type, 0); + if (!type_string) { + return ARGON2_INCORRECT_TYPE; + } + + CC("$"); + CC(type_string); + + /* Reading the version number if the default is suppressed */ + ctx->version = ARGON2_VERSION_10; + CC_opt("$v=", DECIMAL_U32(ctx->version)); + + CC("$m="); + DECIMAL_U32(ctx->m_cost); + CC(",t="); + DECIMAL_U32(ctx->t_cost); + CC(",p="); + DECIMAL_U32(ctx->lanes); + ctx->threads = ctx->lanes; + + CC("$"); + BIN(ctx->salt, maxsaltlen, ctx->saltlen); + CC("$"); + BIN(ctx->out, maxoutlen, ctx->outlen); + + /* The rest of the fields get the default values */ + ctx->secret = NULL; + ctx->secretlen = 0; + ctx->ad = NULL; + ctx->adlen = 0; + ctx->allocate_cbk = NULL; + ctx->free_cbk = NULL; + ctx->flags = ARGON2_DEFAULT_FLAGS; + + /* On return, must have valid context */ + validation_result = validate_inputs(ctx); + if (validation_result != ARGON2_OK) { + return validation_result; + } + + /* Can't have any additional characters */ + if (*str == 0) { + return ARGON2_OK; + } else { + return ARGON2_DECODING_FAIL; + } +#undef CC +#undef CC_opt +#undef DECIMAL +#undef BIN +} + +int encode_string(char *dst, size_t dst_len, argon2_context *ctx, + argon2_type type) { +#define SS(str) \ + do { \ + size_t pp_len = strlen(str); \ + if (pp_len >= dst_len) { \ + return ARGON2_ENCODING_FAIL; \ + } \ + memcpy(dst, str, pp_len + 1); \ + dst += pp_len; \ + dst_len -= pp_len; \ + } while ((void)0, 0) + +#define SX(x) \ + do { \ + char tmp[30]; \ + sprintf(tmp, "%lu", (unsigned long)(x)); \ + SS(tmp); \ + } while ((void)0, 0) + +#define SB(buf, len) \ + do { \ + size_t sb_len = to_base64(dst, dst_len, buf, len); \ + if (sb_len == (size_t)-1) { \ + return ARGON2_ENCODING_FAIL; \ + } \ + dst += sb_len; \ + dst_len -= sb_len; \ + } while ((void)0, 0) + + const char* type_string = argon2_type2string(type, 0); + int validation_result = validate_inputs(ctx); + + if (!type_string) { + return ARGON2_ENCODING_FAIL; + } + + if (validation_result != ARGON2_OK) { + return validation_result; + } + + + SS("$"); + SS(type_string); + + SS("$v="); + SX(ctx->version); + + SS("$m="); + SX(ctx->m_cost); + SS(",t="); + SX(ctx->t_cost); + SS(",p="); + SX(ctx->lanes); + + SS("$"); + SB(ctx->salt, ctx->saltlen); + + SS("$"); + SB(ctx->out, ctx->outlen); + return ARGON2_OK; + +#undef SS +#undef SX +#undef SB +} + +size_t b64len(uint32_t len) { + size_t olen = ((size_t)len / 3) << 2; + + switch (len % 3) { + case 2: + olen++; + /* fall through */ + case 1: + olen += 2; + break; + } + + return olen; +} + +size_t numlen(uint32_t num) { + size_t len = 1; + while (num >= 10) { + ++len; + num = num / 10; + } + return len; +} + diff --git a/framework/3rd/argon2/src/encoding.h b/framework/3rd/argon2/src/encoding.h new file mode 100755 index 0000000..5b8b2dd --- /dev/null +++ b/framework/3rd/argon2/src/encoding.h @@ -0,0 +1,57 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ENCODING_H +#define ENCODING_H +#include "argon2.h" + +#define ARGON2_MAX_DECODED_LANES UINT32_C(255) +#define ARGON2_MIN_DECODED_SALT_LEN UINT32_C(8) +#define ARGON2_MIN_DECODED_OUT_LEN UINT32_C(12) + +/* +* encode an Argon2 hash string into the provided buffer. 'dst_len' +* contains the size, in characters, of the 'dst' buffer; if 'dst_len' +* is less than the number of required characters (including the +* terminating 0), then this function returns ARGON2_ENCODING_ERROR. +* +* on success, ARGON2_OK is returned. +*/ +int encode_string(char *dst, size_t dst_len, argon2_context *ctx, + argon2_type type); + +/* +* Decodes an Argon2 hash string into the provided structure 'ctx'. +* The only fields that must be set prior to this call are ctx.saltlen and +* ctx.outlen (which must be the maximal salt and out length values that are +* allowed), ctx.salt and ctx.out (which must be buffers of the specified +* length), and ctx.pwd and ctx.pwdlen which must hold a valid password. +* +* Invalid input string causes an error. On success, the ctx is valid and all +* fields have been initialized. +* +* Returned value is ARGON2_OK on success, other ARGON2_ codes on error. +*/ +int decode_string(argon2_context *ctx, const char *str, argon2_type type); + +/* Returns the length of the encoded byte stream with length len */ +size_t b64len(uint32_t len); + +/* Returns the length of the encoded number num */ +size_t numlen(uint32_t num); + +#endif diff --git a/framework/3rd/argon2/src/opt.c b/framework/3rd/argon2/src/opt.c new file mode 100755 index 0000000..6c5e403 --- /dev/null +++ b/framework/3rd/argon2/src/opt.c @@ -0,0 +1,283 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include + +#include "argon2.h" +#include "core.h" + +#include "blake2/blake2.h" +#include "blake2/blamka-round-opt.h" + +/* + * Function fills a new memory block and optionally XORs the old block over the new one. + * Memory must be initialized. + * @param state Pointer to the just produced block. Content will be updated(!) + * @param ref_block Pointer to the reference block + * @param next_block Pointer to the block to be XORed over. May coincide with @ref_block + * @param with_xor Whether to XOR into the new block (1) or just overwrite (0) + * @pre all block pointers must be valid + */ +#if defined(__AVX512F__) +static void fill_block(__m512i *state, const block *ref_block, + block *next_block, int with_xor) { + __m512i block_XY[ARGON2_512BIT_WORDS_IN_BLOCK]; + unsigned int i; + + if (with_xor) { + for (i = 0; i < ARGON2_512BIT_WORDS_IN_BLOCK; i++) { + state[i] = _mm512_xor_si512( + state[i], _mm512_loadu_si512((const __m512i *)ref_block->v + i)); + block_XY[i] = _mm512_xor_si512( + state[i], _mm512_loadu_si512((const __m512i *)next_block->v + i)); + } + } else { + for (i = 0; i < ARGON2_512BIT_WORDS_IN_BLOCK; i++) { + block_XY[i] = state[i] = _mm512_xor_si512( + state[i], _mm512_loadu_si512((const __m512i *)ref_block->v + i)); + } + } + + for (i = 0; i < 2; ++i) { + BLAKE2_ROUND_1( + state[8 * i + 0], state[8 * i + 1], state[8 * i + 2], state[8 * i + 3], + state[8 * i + 4], state[8 * i + 5], state[8 * i + 6], state[8 * i + 7]); + } + + for (i = 0; i < 2; ++i) { + BLAKE2_ROUND_2( + state[2 * 0 + i], state[2 * 1 + i], state[2 * 2 + i], state[2 * 3 + i], + state[2 * 4 + i], state[2 * 5 + i], state[2 * 6 + i], state[2 * 7 + i]); + } + + for (i = 0; i < ARGON2_512BIT_WORDS_IN_BLOCK; i++) { + state[i] = _mm512_xor_si512(state[i], block_XY[i]); + _mm512_storeu_si512((__m512i *)next_block->v + i, state[i]); + } +} +#elif defined(__AVX2__) +static void fill_block(__m256i *state, const block *ref_block, + block *next_block, int with_xor) { + __m256i block_XY[ARGON2_HWORDS_IN_BLOCK]; + unsigned int i; + + if (with_xor) { + for (i = 0; i < ARGON2_HWORDS_IN_BLOCK; i++) { + state[i] = _mm256_xor_si256( + state[i], _mm256_loadu_si256((const __m256i *)ref_block->v + i)); + block_XY[i] = _mm256_xor_si256( + state[i], _mm256_loadu_si256((const __m256i *)next_block->v + i)); + } + } else { + for (i = 0; i < ARGON2_HWORDS_IN_BLOCK; i++) { + block_XY[i] = state[i] = _mm256_xor_si256( + state[i], _mm256_loadu_si256((const __m256i *)ref_block->v + i)); + } + } + + for (i = 0; i < 4; ++i) { + BLAKE2_ROUND_1(state[8 * i + 0], state[8 * i + 4], state[8 * i + 1], state[8 * i + 5], + state[8 * i + 2], state[8 * i + 6], state[8 * i + 3], state[8 * i + 7]); + } + + for (i = 0; i < 4; ++i) { + BLAKE2_ROUND_2(state[ 0 + i], state[ 4 + i], state[ 8 + i], state[12 + i], + state[16 + i], state[20 + i], state[24 + i], state[28 + i]); + } + + for (i = 0; i < ARGON2_HWORDS_IN_BLOCK; i++) { + state[i] = _mm256_xor_si256(state[i], block_XY[i]); + _mm256_storeu_si256((__m256i *)next_block->v + i, state[i]); + } +} +#else +static void fill_block(__m128i *state, const block *ref_block, + block *next_block, int with_xor) { + __m128i block_XY[ARGON2_OWORDS_IN_BLOCK]; + unsigned int i; + + if (with_xor) { + for (i = 0; i < ARGON2_OWORDS_IN_BLOCK; i++) { + state[i] = _mm_xor_si128( + state[i], _mm_loadu_si128((const __m128i *)ref_block->v + i)); + block_XY[i] = _mm_xor_si128( + state[i], _mm_loadu_si128((const __m128i *)next_block->v + i)); + } + } else { + for (i = 0; i < ARGON2_OWORDS_IN_BLOCK; i++) { + block_XY[i] = state[i] = _mm_xor_si128( + state[i], _mm_loadu_si128((const __m128i *)ref_block->v + i)); + } + } + + for (i = 0; i < 8; ++i) { + BLAKE2_ROUND(state[8 * i + 0], state[8 * i + 1], state[8 * i + 2], + state[8 * i + 3], state[8 * i + 4], state[8 * i + 5], + state[8 * i + 6], state[8 * i + 7]); + } + + for (i = 0; i < 8; ++i) { + BLAKE2_ROUND(state[8 * 0 + i], state[8 * 1 + i], state[8 * 2 + i], + state[8 * 3 + i], state[8 * 4 + i], state[8 * 5 + i], + state[8 * 6 + i], state[8 * 7 + i]); + } + + for (i = 0; i < ARGON2_OWORDS_IN_BLOCK; i++) { + state[i] = _mm_xor_si128(state[i], block_XY[i]); + _mm_storeu_si128((__m128i *)next_block->v + i, state[i]); + } +} +#endif + +static void next_addresses(block *address_block, block *input_block) { + /*Temporary zero-initialized blocks*/ +#if defined(__AVX512F__) + __m512i zero_block[ARGON2_512BIT_WORDS_IN_BLOCK]; + __m512i zero2_block[ARGON2_512BIT_WORDS_IN_BLOCK]; +#elif defined(__AVX2__) + __m256i zero_block[ARGON2_HWORDS_IN_BLOCK]; + __m256i zero2_block[ARGON2_HWORDS_IN_BLOCK]; +#else + __m128i zero_block[ARGON2_OWORDS_IN_BLOCK]; + __m128i zero2_block[ARGON2_OWORDS_IN_BLOCK]; +#endif + + memset(zero_block, 0, sizeof(zero_block)); + memset(zero2_block, 0, sizeof(zero2_block)); + + /*Increasing index counter*/ + input_block->v[6]++; + + /*First iteration of G*/ + fill_block(zero_block, input_block, address_block, 0); + + /*Second iteration of G*/ + fill_block(zero2_block, address_block, address_block, 0); +} + +void fill_segment(const argon2_instance_t *instance, + argon2_position_t position) { + block *ref_block = NULL, *curr_block = NULL; + block address_block, input_block; + uint64_t pseudo_rand, ref_index, ref_lane; + uint32_t prev_offset, curr_offset; + uint32_t starting_index, i; +#if defined(__AVX512F__) + __m512i state[ARGON2_512BIT_WORDS_IN_BLOCK]; +#elif defined(__AVX2__) + __m256i state[ARGON2_HWORDS_IN_BLOCK]; +#else + __m128i state[ARGON2_OWORDS_IN_BLOCK]; +#endif + int data_independent_addressing; + + if (instance == NULL) { + return; + } + + data_independent_addressing = + (instance->type == Argon2_i) || + (instance->type == Argon2_id && (position.pass == 0) && + (position.slice < ARGON2_SYNC_POINTS / 2)); + + if (data_independent_addressing) { + init_block_value(&input_block, 0); + + input_block.v[0] = position.pass; + input_block.v[1] = position.lane; + input_block.v[2] = position.slice; + input_block.v[3] = instance->memory_blocks; + input_block.v[4] = instance->passes; + input_block.v[5] = instance->type; + } + + starting_index = 0; + + if ((0 == position.pass) && (0 == position.slice)) { + starting_index = 2; /* we have already generated the first two blocks */ + + /* Don't forget to generate the first block of addresses: */ + if (data_independent_addressing) { + next_addresses(&address_block, &input_block); + } + } + + /* Offset of the current block */ + curr_offset = position.lane * instance->lane_length + + position.slice * instance->segment_length + starting_index; + + if (0 == curr_offset % instance->lane_length) { + /* Last block in this lane */ + prev_offset = curr_offset + instance->lane_length - 1; + } else { + /* Previous block */ + prev_offset = curr_offset - 1; + } + + memcpy(state, ((instance->memory + prev_offset)->v), ARGON2_BLOCK_SIZE); + + for (i = starting_index; i < instance->segment_length; + ++i, ++curr_offset, ++prev_offset) { + /*1.1 Rotating prev_offset if needed */ + if (curr_offset % instance->lane_length == 1) { + prev_offset = curr_offset - 1; + } + + /* 1.2 Computing the index of the reference block */ + /* 1.2.1 Taking pseudo-random value from the previous block */ + if (data_independent_addressing) { + if (i % ARGON2_ADDRESSES_IN_BLOCK == 0) { + next_addresses(&address_block, &input_block); + } + pseudo_rand = address_block.v[i % ARGON2_ADDRESSES_IN_BLOCK]; + } else { + pseudo_rand = instance->memory[prev_offset].v[0]; + } + + /* 1.2.2 Computing the lane of the reference block */ + ref_lane = ((pseudo_rand >> 32)) % instance->lanes; + + if ((position.pass == 0) && (position.slice == 0)) { + /* Can not reference other lanes yet */ + ref_lane = position.lane; + } + + /* 1.2.3 Computing the number of possible reference block within the + * lane. + */ + position.index = i; + ref_index = index_alpha(instance, &position, pseudo_rand & 0xFFFFFFFF, + ref_lane == position.lane); + + /* 2 Creating a new block */ + ref_block = + instance->memory + instance->lane_length * ref_lane + ref_index; + curr_block = instance->memory + curr_offset; + if (ARGON2_VERSION_10 == instance->version) { + /* version 1.2.1 and earlier: overwrite, not XOR */ + fill_block(state, ref_block, curr_block, 0); + } else { + if(0 == position.pass) { + fill_block(state, ref_block, curr_block, 0); + } else { + fill_block(state, ref_block, curr_block, 1); + } + } + } +} diff --git a/framework/3rd/argon2/src/ref.c b/framework/3rd/argon2/src/ref.c new file mode 100755 index 0000000..10e45eb --- /dev/null +++ b/framework/3rd/argon2/src/ref.c @@ -0,0 +1,194 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include + +#include "argon2.h" +#include "core.h" + +#include "blake2/blamka-round-ref.h" +#include "blake2/blake2-impl.h" +#include "blake2/blake2.h" + + +/* + * Function fills a new memory block and optionally XORs the old block over the new one. + * @next_block must be initialized. + * @param prev_block Pointer to the previous block + * @param ref_block Pointer to the reference block + * @param next_block Pointer to the block to be constructed + * @param with_xor Whether to XOR into the new block (1) or just overwrite (0) + * @pre all block pointers must be valid + */ +static void fill_block(const block *prev_block, const block *ref_block, + block *next_block, int with_xor) { + block blockR, block_tmp; + unsigned i; + + copy_block(&blockR, ref_block); + xor_block(&blockR, prev_block); + copy_block(&block_tmp, &blockR); + /* Now blockR = ref_block + prev_block and block_tmp = ref_block + prev_block */ + if (with_xor) { + /* Saving the next block contents for XOR over: */ + xor_block(&block_tmp, next_block); + /* Now blockR = ref_block + prev_block and + block_tmp = ref_block + prev_block + next_block */ + } + + /* Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then + (16,17,..31)... finally (112,113,...127) */ + for (i = 0; i < 8; ++i) { + BLAKE2_ROUND_NOMSG( + blockR.v[16 * i], blockR.v[16 * i + 1], blockR.v[16 * i + 2], + blockR.v[16 * i + 3], blockR.v[16 * i + 4], blockR.v[16 * i + 5], + blockR.v[16 * i + 6], blockR.v[16 * i + 7], blockR.v[16 * i + 8], + blockR.v[16 * i + 9], blockR.v[16 * i + 10], blockR.v[16 * i + 11], + blockR.v[16 * i + 12], blockR.v[16 * i + 13], blockR.v[16 * i + 14], + blockR.v[16 * i + 15]); + } + + /* Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then + (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127) */ + for (i = 0; i < 8; i++) { + BLAKE2_ROUND_NOMSG( + blockR.v[2 * i], blockR.v[2 * i + 1], blockR.v[2 * i + 16], + blockR.v[2 * i + 17], blockR.v[2 * i + 32], blockR.v[2 * i + 33], + blockR.v[2 * i + 48], blockR.v[2 * i + 49], blockR.v[2 * i + 64], + blockR.v[2 * i + 65], blockR.v[2 * i + 80], blockR.v[2 * i + 81], + blockR.v[2 * i + 96], blockR.v[2 * i + 97], blockR.v[2 * i + 112], + blockR.v[2 * i + 113]); + } + + copy_block(next_block, &block_tmp); + xor_block(next_block, &blockR); +} + +static void next_addresses(block *address_block, block *input_block, + const block *zero_block) { + input_block->v[6]++; + fill_block(zero_block, input_block, address_block, 0); + fill_block(zero_block, address_block, address_block, 0); +} + +void fill_segment(const argon2_instance_t *instance, + argon2_position_t position) { + block *ref_block = NULL, *curr_block = NULL; + block address_block, input_block, zero_block; + uint64_t pseudo_rand, ref_index, ref_lane; + uint32_t prev_offset, curr_offset; + uint32_t starting_index; + uint32_t i; + int data_independent_addressing; + + if (instance == NULL) { + return; + } + + data_independent_addressing = + (instance->type == Argon2_i) || + (instance->type == Argon2_id && (position.pass == 0) && + (position.slice < ARGON2_SYNC_POINTS / 2)); + + if (data_independent_addressing) { + init_block_value(&zero_block, 0); + init_block_value(&input_block, 0); + + input_block.v[0] = position.pass; + input_block.v[1] = position.lane; + input_block.v[2] = position.slice; + input_block.v[3] = instance->memory_blocks; + input_block.v[4] = instance->passes; + input_block.v[5] = instance->type; + } + + starting_index = 0; + + if ((0 == position.pass) && (0 == position.slice)) { + starting_index = 2; /* we have already generated the first two blocks */ + + /* Don't forget to generate the first block of addresses: */ + if (data_independent_addressing) { + next_addresses(&address_block, &input_block, &zero_block); + } + } + + /* Offset of the current block */ + curr_offset = position.lane * instance->lane_length + + position.slice * instance->segment_length + starting_index; + + if (0 == curr_offset % instance->lane_length) { + /* Last block in this lane */ + prev_offset = curr_offset + instance->lane_length - 1; + } else { + /* Previous block */ + prev_offset = curr_offset - 1; + } + + for (i = starting_index; i < instance->segment_length; + ++i, ++curr_offset, ++prev_offset) { + /*1.1 Rotating prev_offset if needed */ + if (curr_offset % instance->lane_length == 1) { + prev_offset = curr_offset - 1; + } + + /* 1.2 Computing the index of the reference block */ + /* 1.2.1 Taking pseudo-random value from the previous block */ + if (data_independent_addressing) { + if (i % ARGON2_ADDRESSES_IN_BLOCK == 0) { + next_addresses(&address_block, &input_block, &zero_block); + } + pseudo_rand = address_block.v[i % ARGON2_ADDRESSES_IN_BLOCK]; + } else { + pseudo_rand = instance->memory[prev_offset].v[0]; + } + + /* 1.2.2 Computing the lane of the reference block */ + ref_lane = ((pseudo_rand >> 32)) % instance->lanes; + + if ((position.pass == 0) && (position.slice == 0)) { + /* Can not reference other lanes yet */ + ref_lane = position.lane; + } + + /* 1.2.3 Computing the number of possible reference block within the + * lane. + */ + position.index = i; + ref_index = index_alpha(instance, &position, pseudo_rand & 0xFFFFFFFF, + ref_lane == position.lane); + + /* 2 Creating a new block */ + ref_block = + instance->memory + instance->lane_length * ref_lane + ref_index; + curr_block = instance->memory + curr_offset; + if (ARGON2_VERSION_10 == instance->version) { + /* version 1.2.1 and earlier: overwrite, not XOR */ + fill_block(instance->memory + prev_offset, ref_block, curr_block, 0); + } else { + if(0 == position.pass) { + fill_block(instance->memory + prev_offset, ref_block, + curr_block, 0); + } else { + fill_block(instance->memory + prev_offset, ref_block, + curr_block, 1); + } + } + } +} diff --git a/framework/3rd/argon2/src/thread.c b/framework/3rd/argon2/src/thread.c new file mode 100755 index 0000000..3ae2fb2 --- /dev/null +++ b/framework/3rd/argon2/src/thread.c @@ -0,0 +1,57 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#if !defined(ARGON2_NO_THREADS) + +#include "thread.h" +#if defined(_WIN32) +#include +#endif + +int argon2_thread_create(argon2_thread_handle_t *handle, + argon2_thread_func_t func, void *args) { + if (NULL == handle || func == NULL) { + return -1; + } +#if defined(_WIN32) + *handle = _beginthreadex(NULL, 0, func, args, 0, NULL); + return *handle != 0 ? 0 : -1; +#else + return pthread_create(handle, NULL, func, args); +#endif +} + +int argon2_thread_join(argon2_thread_handle_t handle) { +#if defined(_WIN32) + if (WaitForSingleObject((HANDLE)handle, INFINITE) == WAIT_OBJECT_0) { + return CloseHandle((HANDLE)handle) != 0 ? 0 : -1; + } + return -1; +#else + return pthread_join(handle, NULL); +#endif +} + +void argon2_thread_exit(void) { +#if defined(_WIN32) + _endthreadex(0); +#else + pthread_exit(NULL); +#endif +} + +#endif /* ARGON2_NO_THREADS */ diff --git a/framework/3rd/argon2/src/thread.h b/framework/3rd/argon2/src/thread.h new file mode 100755 index 0000000..d4ca10c --- /dev/null +++ b/framework/3rd/argon2/src/thread.h @@ -0,0 +1,67 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ARGON2_THREAD_H +#define ARGON2_THREAD_H + +#if !defined(ARGON2_NO_THREADS) + +/* + Here we implement an abstraction layer for the simpĺe requirements + of the Argon2 code. We only require 3 primitives---thread creation, + joining, and termination---so full emulation of the pthreads API + is unwarranted. Currently we wrap pthreads and Win32 threads. + + The API defines 2 types: the function pointer type, + argon2_thread_func_t, + and the type of the thread handle---argon2_thread_handle_t. +*/ +#if defined(_WIN32) +#include +typedef unsigned(__stdcall *argon2_thread_func_t)(void *); +typedef uintptr_t argon2_thread_handle_t; +#else +#include +typedef void *(*argon2_thread_func_t)(void *); +typedef pthread_t argon2_thread_handle_t; +#endif + +/* Creates a thread + * @param handle pointer to a thread handle, which is the output of this + * function. Must not be NULL. + * @param func A function pointer for the thread's entry point. Must not be + * NULL. + * @param args Pointer that is passed as an argument to @func. May be NULL. + * @return 0 if @handle and @func are valid pointers and a thread is successfully + * created. + */ +int argon2_thread_create(argon2_thread_handle_t *handle, + argon2_thread_func_t func, void *args); + +/* Waits for a thread to terminate + * @param handle Handle to a thread created with argon2_thread_create. + * @return 0 if @handle is a valid handle, and joining completed successfully. +*/ +int argon2_thread_join(argon2_thread_handle_t handle); + +/* Terminate the current thread. Must be run inside a thread created by + * argon2_thread_create. +*/ +void argon2_thread_exit(void); + +#endif /* ARGON2_NO_THREADS */ +#endif diff --git a/framework/3rd/readme.txt b/framework/3rd/readme.txt new file mode 100644 index 0000000..e69de29 diff --git a/framework/Makefile b/framework/Makefile new file mode 100644 index 0000000..68904bb --- /dev/null +++ b/framework/Makefile @@ -0,0 +1,32 @@ +SKYNET_ROOT ?= ./skynet/ +SKYNET_DEPS ?= ./lualib-src/ + +PLAT ?= linux +TLS_INC ?= /usr/local/openssl/include +TLS_LIB ?= /usr/local/openssl/lib + +ifeq ($(PLAT), macosx) + TLS_INC=/usr/local/opt/openssl@1.1/include + TLS_LIB=/usr/local/opt/openssl@1.1/lib +endif + +.PHONY: all + +all : runtime deps + +runtime: + git submodule update --init --recursive + $(MAKE) cleanall + cd $(SKYNET_ROOT) && $(MAKE) $(PLAT) TLS_MODULE=ltls TLS_INC=$(TLS_INC) TLS_LIB=$(TLS_LIB) -j8 + +deps: + cd $(SKYNET_DEPS) && $(MAKE) $(PLAT) + +cleanall: + cd $(SKYNET_ROOT) && $(MAKE) cleanall + cd $(SKYNET_DEPS) && $(MAKE) cleanall + +testluaclib: + $(SKYNET_ROOT)3rd/lua/lua $(SKYNET_DEPS)testluaclib.lua +#-------------------------------------------------------------# +#-------------------------------------------------------------# diff --git a/framework/luaclib/.gitkeep b/framework/luaclib/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/framework/lualib-src/Makefile b/framework/lualib-src/Makefile new file mode 100644 index 0000000..06a9e8a --- /dev/null +++ b/framework/lualib-src/Makefile @@ -0,0 +1,81 @@ +SKYNET_ROOT ?= ../skynet +include $(SKYNET_ROOT)/platform.mk + +PLAT ?= linux + +##################################################### +SKYNET_LUA_INC ?= $(SKYNET_ROOT)/3rd/lua/ +SKYNET_LUA_LIB ?= $(SKYNET_ROOT)/3rd/lua/ +SKYNET_SRC ?= $(SKYNET_ROOT)/skynet-src + +LFLAGS = $(SHARED) -I$(SKYNET_LUA_INC) + +LUA_CLIB_PATH ?= ../luaclib +##################################################### + +LFS_SO = $(LUA_CLIB_PATH)/lfs.so +CJSON_SO = $(LUA_CLIB_PATH)/cjson.so +PROFILE_SO = $(LUA_CLIB_PATH)/profile.so +SKIPLIST_SO = $(LUA_CLIB_PATH)/skiplist.so +SNAPSHOT_SO = $(LUA_CLIB_PATH)/snapshot.so +SHIFTTIMER_SO = $(LUA_CLIB_PATH)/shiftimer.so +GEOHASH_SO = $(LUA_CLIB_PATH)/geohash.so +CRAB_SO = $(LUA_CLIB_PATH)/crab.so +UTF8_SO = $(LUA_CLIB_PATH)/utf8.so +CLUA_SO = $(LUA_CLIB_PATH)/clua.so +FMT_SO = $(LUA_CLIB_PATH)/lfmt.so +##################################################### +all: $(LFS_SO) \ + $(CJSON_SO) \ + $(PROFILE_SO) \ + $(SKIPLIST_SO) \ + $(SNAPSHOT_SO) \ + $(SHIFTTIMER_SO) \ + $(CRAB_SO) \ + $(UTF8_SO) \ + $(CLUA_SO) \ + $(GEOHASH_SO) \ + $(FMT_SO) +##################################################### + +$(LFS_SO): + cd lua-lfs && $(MAKE) PLAT=$(PLAT) + +$(CJSON_SO): + cd lua-cjson && $(MAKE) PLAT=$(PLAT) + +$(PROFILE_SO): + cd lua-profile && $(MAKE) PLAT=$(PLAT) + +$(SKIPLIST_SO): + cd lua-zset && $(MAKE) PLAT=$(PLAT) + +$(SNAPSHOT_SO): + cd lua-snapshot && $(MAKE) PLAT=$(PLAT) + +$(SHIFTTIMER_SO): + cd lua-timer && $(MAKE) PLAT=$(PLAT) + +$(GEOHASH_SO): + cd lua-geohash && $(MAKE) PLAT=$(PLAT) + +$(CRAB_SO): + cd lua-crab && $(MAKE) PLAT=$(PLAT) + +$(UTF8_SO): + cd lua-utf8 && $(MAKE) PLAT=$(PLAT) + +$(CLUA_SO): + cd lua-clua && $(MAKE) PLAT=$(PLAT) + +$(FMT_SO): + cd lua-fmt && $(MAKE) PLAT=$(PLAT) + +##################################################### + +cleanall: + rm -f $(LUA_CLIB_PATH)/*.so && \ + rm -rf $(LUA_CLIB_PATH)/*.dSYM && \ + rm -f $(LUA_CLIB_PATH)/*.o + +.PHONY: all cleanall diff --git a/framework/lualib-src/lua-argon2/Makefile b/framework/lualib-src/lua-argon2/Makefile new file mode 100755 index 0000000..d4c3aeb --- /dev/null +++ b/framework/lualib-src/lua-argon2/Makefile @@ -0,0 +1,35 @@ +SRC = luabinding.c src/argon2.c src/core.c src/blake2/blake2b.c src/thread.c src/encoding.c + +CC := gcc +CFLAGS += -O3 -Wall -g -Iinclude +ifeq ($(NO_THREADS), 1) +CFLAGS += -DARGON2_NO_THREADS +else +CFLAGS += -pthread +endif + +# x86 cpu-type https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html +OPTTARGET ?= native +OPTTEST := $(shell $(CC) -Iinclude -Isrc -march=$(OPTTARGET) src/opt.c -c \ + -o /dev/null 2>/dev/null; echo $$?) +# Detect compatible platform +ifneq ($(OPTTEST), 0) +$(info Building without optimizations) + SRC += src/ref.c +else +$(info Building with optimizations for $(OPTTARGET)) + CFLAGS += -march=$(OPTTARGET) + SRC += src/opt.c +endif + +LIB_NAME = argon2 +SHARED := -shared -fPIC -DA2_VISCTL=1 + +.PHONY: all +all: $(LIB_NAME).so + +$(LIB_NAME).so: $(SRC) + $(CC) $(CFLAGS) $(SHARED) $^ -o $@ + +clean: + rm -f $(LIB_NAME).so diff --git a/framework/lualib-src/lua-argon2/README.md b/framework/lualib-src/lua-argon2/README.md new file mode 100644 index 0000000..8fccb5f --- /dev/null +++ b/framework/lualib-src/lua-argon2/README.md @@ -0,0 +1,9 @@ +# lua-argon2 + +lua banding for argon2 + +https://github.com/rangercyh/lua-argon2 + +https://github.com/p-h-c/phc-winner-argon2 + +https://github.com/thibaultcha/lua-argon2 diff --git a/framework/lualib-src/lua-argon2/luabinding.c b/framework/lualib-src/lua-argon2/luabinding.c new file mode 100755 index 0000000..d0841ef --- /dev/null +++ b/framework/lualib-src/lua-argon2/luabinding.c @@ -0,0 +1,411 @@ +#include +#include +#include +#include +#include "argon2.h" + +#ifndef LUA_51 +#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 +#define LUA_51 1 +#else +#define LUA_51 0 +#endif +#endif + +#define LUA_ARGON2_DEFAULT_T_COST 3 +#define LUA_ARGON2_DEFAULT_M_COST 4096 +#define LUA_ARGON2_DEFAULT_PARALLELISM 1 +#define LUA_ARGON2_DEFAULT_HASH_LEN 32 + +typedef struct largon2_config_s largon2_config_t; +struct largon2_config_s { + uint32_t m_cost; + uint32_t t_cost; + uint32_t parallelism; + uint32_t hash_len; + argon2_type variant; +}; + +static void +largon2_create_config(lua_State *L) +{ + largon2_config_t *cfg; + + cfg = lua_newuserdata(L, sizeof(*cfg)); + cfg->t_cost = LUA_ARGON2_DEFAULT_T_COST; + cfg->m_cost = LUA_ARGON2_DEFAULT_M_COST; + cfg->parallelism = LUA_ARGON2_DEFAULT_PARALLELISM; + cfg->hash_len = LUA_ARGON2_DEFAULT_HASH_LEN; + cfg->variant = Argon2_i; +} + +static largon2_config_t * +largon2_fetch_config(lua_State *L) +{ + largon2_config_t *cfg; + + cfg = lua_touserdata(L, lua_upvalueindex(1)); + if (!cfg) { + luaL_error(L, "could not retrieve argon2 config"); + } + + return cfg; +} + +static largon2_config_t * +largon2_arg_init(lua_State *L, int nargs) +{ + if (lua_gettop(L) > nargs) { + luaL_error(L, "expecting no more than %d arguments, but got %d", + nargs, lua_gettop(L)); + } + + lua_settop(L, nargs); + + return largon2_fetch_config(L); +} + +static void +largon2_integer_opt(lua_State *L, uint32_t optidx, uint32_t argidx, + uint32_t *property, const char *key) +{ + uint32_t value; + char errmsg[64]; + + if (!lua_isnil(L, optidx)) { + if (lua_isnumber(L, optidx)) { + value = lua_tonumber(L, optidx); + *property = value; + } else { + sprintf(errmsg, "expected %s to be a number, got %s", + key, luaL_typename(L, optidx)); + luaL_argerror(L, argidx, errmsg); + } + } +} + +/* +t_cost Number of iterations (`number`, default: `3`) +argon2.t_cost(4) +argon2.hash_encoded("password", "salt", { t_cost = 4 }) +*/ +static int +largon2_cfg_t_cost(lua_State *L) +{ + largon2_config_t *cfg = largon2_arg_init(L, 1); + + largon2_integer_opt(L, 1, 1, &cfg->t_cost, "t_cost"); + lua_pushinteger(L, cfg->t_cost); + + return 1; +} + +/* +m_cost Sets memory usage as KiB (`number`, default: `4096`) +argon2.m_cost(16) +argon2.hash_encoded("password", "salt", { + m_cost = math.pow(2, 16) -- 2^16 aka 65536 KiB +}) +*/ +static int +largon2_cfg_m_cost(lua_State *L) +{ + largon2_config_t *cfg = largon2_arg_init(L, 1); + + largon2_integer_opt(L, 1, 1, &cfg->m_cost, "m_cost"); + lua_pushinteger(L, cfg->m_cost); + + return 1; +} + +/* +parallelism Number of threads and compute lanes (`number`, default: `1`) +argon2.parallelism(2) +argon2.hash_encoded("password", "salt", { parallelism = 2 }) +*/ +static int +largon2_cfg_parallelism(lua_State *L) +{ + largon2_config_t *cfg = largon2_arg_init(L, 1); + + largon2_integer_opt(L, 1, 1, &cfg->parallelism, "parallelism"); + lua_pushinteger(L, cfg->parallelism); + + return 1; +} + +/* +hash_len Length of the hash output length (`number`, default: `32`) +argon2.hash_len(64) +argon2.hash_encoded("password", "salt", { hash_len = 64 }) +*/ +static int +largon2_cfg_hash_len(lua_State *L) +{ + largon2_config_t *cfg = largon2_arg_init(L, 1); + + largon2_integer_opt(L, 1, 1, &cfg->hash_len, "hash_len"); + lua_pushinteger(L, cfg->hash_len); + + return 1; +} + +/* +variant Choose the Argon2 variant to use (Argon2i, Argon2d, Argon2id) +from the `variants` table. (`userdata`, default: `argon2.variants.argon2_i`). +argon2.variant(argon2.variants.argon2_i) +argon2.variant(argon2.variants.argon2_d) +argon2.variant(argon2.variants.argon2_id) +argon2.hash_encoded("password", "salt", { variant = argon2.variants.argon2_d }) +*/ +static int +largon2_cfg_variant(lua_State *L) +{ + largon2_config_t *cfg = largon2_arg_init(L, 1); + + luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); + + cfg->variant = (argon2_type) lua_touserdata(L, 1); + + return 1; +} + +/* +local hash, err = argon2.hash_encoded("password", "somesalt") +if err then + error("could not hash_encoded: " .. err) +end + +-- with options and variant +local hash, err = argon2.hash_encoded("password", "somesalt", { + t_cost = 4, + m_cost = math.pow(2, 16), -- 65536 KiB + variant = argon2.variants.argon2_d +}) +*/ +static int +largon2_hash_encoded(lua_State *L) +{ + const char *plain, *salt; + char *encoded, *err_msg; + size_t plainlen, saltlen; + size_t encoded_len; + uint32_t t_cost; + uint32_t m_cost; + uint32_t hash_len; + uint32_t parallelism; + argon2_type variant; + argon2_error_codes ret_code; + largon2_config_t *cfg; + luaL_Buffer buf; + + plain = luaL_checklstring(L, 1, &plainlen); + salt = luaL_checklstring(L, 2, &saltlen); + + cfg = largon2_arg_init(L, 3); + + t_cost = cfg->t_cost; + m_cost = cfg->m_cost; + parallelism = cfg->parallelism; + hash_len = cfg->hash_len; + variant = cfg->variant; + + if (!lua_isnil(L, 3)) { + if (!lua_istable(L, 3)) { + luaL_argerror(L, 3, "expected to be a table"); + } + + lua_getfield(L, 3, "t_cost"); + largon2_integer_opt(L, -1, 3, &t_cost, "t_cost"); + lua_pop(L, 1); + + lua_getfield(L, 3, "m_cost"); + largon2_integer_opt(L, -1, 3, &m_cost, "m_cost"); + lua_pop(L, 1); + + lua_getfield(L, 3, "parallelism"); + largon2_integer_opt(L, -1, 3, ¶llelism, "parallelism"); + lua_pop(L, 1); + + lua_getfield(L, 3, "hash_len"); + largon2_integer_opt(L, -1, 3, &hash_len, "hash_len"); + lua_pop(L, 1); + + lua_getfield(L, 3, "variant"); + if (!lua_isnil(L, -1)) { + if (!lua_islightuserdata(L, -1)) { + char errmsg[64]; + sprintf(errmsg, "expected variant to be a number, got %s", + luaL_typename(L, -1)); + luaL_argerror(L, 3, errmsg); + } + + variant = (argon2_type) lua_touserdata(L, -1); + } + + lua_pop(L, 1); + } + + encoded_len = argon2_encodedlen(t_cost, m_cost, parallelism, saltlen, + hash_len, variant); +#if LUA_51 + luaL_buffinit(L, &buf); + encoded = luaL_prepbuffer(&buf); +#else + encoded = luaL_buffinitsize(L, &buf, encoded_len); +#endif + + if (variant == Argon2_d) { + ret_code = + argon2d_hash_encoded(t_cost, m_cost, parallelism, plain, plainlen, + salt, saltlen, hash_len, encoded, encoded_len); + } else if (variant == Argon2_id) { + ret_code = + argon2id_hash_encoded(t_cost, m_cost, parallelism, plain, plainlen, + salt, saltlen, hash_len, encoded, encoded_len); + } else { + ret_code = + argon2i_hash_encoded(t_cost, m_cost, parallelism, plain, plainlen, + salt, saltlen, hash_len, encoded, encoded_len); + } + +#if LUA_51 + luaL_addsize(&buf, encoded_len); + luaL_pushresult(&buf); +#else + luaL_pushresultsize(&buf, encoded_len); +#endif + + if (ret_code != ARGON2_OK) { + err_msg = (char *) argon2_error_message(ret_code); + lua_pushnil(L); + lua_pushstring(L, err_msg); + return 2; + } + + return 1; +} + +/* +local ok, err = argon2.verify(argon2i_hash, "password") +if err then + -- failure to verify (*not* a password mismatch) + error("could not verify: " .. err) +end + +if not ok then + -- password mismatch + error("The password does not match the supplied hash") +end + +-- with a argon2d hash +local ok, err = argon2.verify(argon2d_hash, "password") +*/ +static int +largon2_verify(lua_State *L) +{ + const char *plain, *encoded; + size_t plainlen; + argon2_type variant; + argon2_error_codes ret_code; + char *err_msg; + + if (lua_gettop(L) != 2) { + return luaL_error(L, "expecting 2 arguments, but got %d", + lua_gettop(L)); + } + + encoded = luaL_checkstring(L, 1); + plain = luaL_checklstring(L, 2, &plainlen); + + if (strstr(encoded, "argon2d")) { + variant = Argon2_d; + } else if (strstr(encoded, "argon2id")) { + variant = Argon2_id; + } else { + variant = Argon2_i; + } + + ret_code = argon2_verify(encoded, plain, plainlen, variant); + if (ret_code == ARGON2_VERIFY_MISMATCH) { + lua_pushboolean(L, 0); + return 1; + } + + if (ret_code != ARGON2_OK) { + err_msg = (char *) argon2_error_message(ret_code); + lua_pushnil(L); + lua_pushstring(L, err_msg); + return 2; + } + + lua_pushboolean(L, 1); + + return 1; +} + +static void +largon2_push_argon2_variants_table(lua_State *L) +{ + lua_newtable(L); + + lua_pushlightuserdata(L, (void *) Argon2_i); + lua_setfield(L, -2, "argon2_i"); + + lua_pushlightuserdata(L, (void *) Argon2_d); + lua_setfield(L, -2, "argon2_d"); + + lua_pushlightuserdata(L, (void *) Argon2_id); + lua_setfield(L, -2, "argon2_id"); + + return; +} + +#if LUA_51 +/* Compatibility for Lua 5.1. + * + * luaL_setfuncs() is used to create a module table where the functions have + * largon2_config_t as their first upvalue. Code borrowed from Lua 5.2 source. */ +static void +compat_luaL_setfuncs(lua_State *l, const luaL_Reg *reg, int nup) +{ + int i; + + luaL_checkstack(l, nup, "too many upvalues"); + for (; reg->name != NULL; reg++) { /* fill the table with given functions */ + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(l, -nup); + lua_pushcclosure(l, reg->func, nup); /* closure with those upvalues */ + lua_setfield(l, -(nup + 2), reg->name); + } + lua_pop(l, nup); /* remove upvalues */ +} +#else +#define compat_luaL_setfuncs(L, l, nup) luaL_setfuncs(L, l, nup) +#endif + +static const luaL_Reg largon2[] = { + { "verify", largon2_verify }, + { "hash_encoded", largon2_hash_encoded }, + { "t_cost", largon2_cfg_t_cost }, + { "m_cost", largon2_cfg_m_cost }, + { "parallelism", largon2_cfg_parallelism }, + { "hash_len", largon2_cfg_hash_len }, + { "variant", largon2_cfg_variant }, + { NULL, NULL } +}; + +int +luaopen_argon2(lua_State *L) +{ + lua_newtable(L); + + largon2_create_config(L); + compat_luaL_setfuncs(L, largon2, 1); + + /* push argon2.variants table */ + largon2_push_argon2_variants_table(L); + lua_setfield(L, -2, "variants"); + + return 1; +} diff --git a/framework/lualib-src/lua-argon2/test.lua b/framework/lualib-src/lua-argon2/test.lua new file mode 100755 index 0000000..23f529e --- /dev/null +++ b/framework/lualib-src/lua-argon2/test.lua @@ -0,0 +1,48 @@ +local argon2 = require "argon2" + +--- Argon2i +print(assert(argon2.hash_encoded("password", "somesalt"))) +-- encoded is "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ$iWh06vD8Fy27wf9npn6FXWiCX4K6pW6Ue1Bnzz07Z8A" + +--- Argon2d +print(assert(argon2.hash_encoded("password", "somesalt", { + variant = argon2.variants.argon2_d +}))) +-- encoded is "$argon2d$v=19$m=4096,t=3,p=1$c29tZXNhbHQ$2+JCoQtY/2x5F0VB9pEVP3xBNguWP1T25Ui0PtZuk8o" + +--- Argon2id +print(assert(argon2.hash_encoded("password", "somesalt", { + variant = argon2.variants.argon2_id +}))) +-- encoded is "$argon2id$v=19$m=4096,t=3,p=1$c29tZXNhbHQ$qLml5cbqFAO6YxVHhrSBHP0UWdxrIxkNcM8aMX3blzU" + +-- Hashing options +print(assert(argon2.hash_encoded("password", "somesalt", { + t_cost = 4, + m_cost = math.pow(2, 16), -- 65536 KiB + parallelism = 2 +}))) +-- encoded is "$argon2i$v=19$m=65536,t=4,p=2$c29tZXNhbHQ$n6x5DKNWV8BOeKemQJRk7BU3hcaCVomtn9TCyEA0inM" + +-- Changing the default options (those arguments are the current defaults) +argon2.t_cost(3) +argon2.m_cost(4096) +argon2.parallelism(1) +argon2.hash_len(32) +argon2.variant(argon2.variants.argon2_i) + + +local encoded = assert(argon2.hash_encoded("password", "somesalt")) +print(encoded) +-- encoded: argon2i encoded hash + +local ok, err = argon2.verify(encoded, "password") +if err then + error("could not verify: " .. err) +end + +if not ok then + error("The password does not match the supplied hash") +end + +print(encoded, "verify ok") diff --git a/framework/lualib-src/lua-cjson/Makefile b/framework/lualib-src/lua-cjson/Makefile new file mode 100644 index 0000000..6745b70 --- /dev/null +++ b/framework/lualib-src/lua-cjson/Makefile @@ -0,0 +1,30 @@ +SKYNET_ROOT ?= ../../skynet +include $(SKYNET_ROOT)/platform.mk + +PLAT ?= none + +TARGET = ../../luaclib/cjson.so + +ifeq ($(PLAT), macosx) + CFLAGS = -g -O2 -dynamiclib -Wl,-undefined,dynamic_lookup +else +ifeq ($(PLAT), linux) + CFLAGS = -g -O2 -shared -fPIC +endif +endif + +LUA_LIB ?= $(SKYNET_ROOT)/3rd/lua/ +LUA_INC ?= $(SKYNET_ROOT)/3rd/lua/ +SKYNET_SRC ?= $(SKYNET_ROOT)/skynet-src + +SRC = . + +.PHONY: all clean + +all: $(TARGET) + +$(TARGET): $(foreach dir, $(SRC), $(wildcard $(dir)/*.c)) + $(CC) $(CFLAGS) $(SHARED) -I$(LUA_INC) -I$(SKYNET_SRC) $^ -o $@ + +clean: + rm -f *.o $(TARGET) \ No newline at end of file diff --git a/framework/lualib-src/lua-cjson/fpconv.c b/framework/lualib-src/lua-cjson/fpconv.c new file mode 100644 index 0000000..2a67a39 --- /dev/null +++ b/framework/lualib-src/lua-cjson/fpconv.c @@ -0,0 +1,205 @@ +/* fpconv - Floating point conversion routines + * + * Copyright (c) 2011-2012 Mark Pulford + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries + * with locale support will break when the decimal separator is a comma. + * + * fpconv_* will around these issues with a translation buffer if required. + */ + +#include +#include +#include +#include + +#include "fpconv.h" + +/* Lua CJSON assumes the locale is the same for all threads within a + * process and doesn't change after initialisation. + * + * This avoids the need for per thread storage or expensive checks + * for call. */ +static char locale_decimal_point = '.'; + +/* In theory multibyte decimal_points are possible, but + * Lua CJSON only supports UTF-8 and known locales only have + * single byte decimal points ([.,]). + * + * localconv() may not be thread safe (=>crash), and nl_langinfo() is + * not supported on some platforms. Use sprintf() instead - if the + * locale does change, at least Lua CJSON won't crash. */ +static void fpconv_update_locale() +{ + char buf[8]; + + snprintf(buf, sizeof(buf), "%g", 0.5); + + /* Failing this test might imply the platform has a buggy dtoa + * implementation or wide characters */ + if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) { + fprintf(stderr, "Error: wide characters found or printf() bug."); + abort(); + } + + locale_decimal_point = buf[1]; +} + +/* Check for a valid number character: [-+0-9a-yA-Y.] + * Eg: -0.6e+5, infinity, 0xF0.F0pF0 + * + * Used to find the probable end of a number. It doesn't matter if + * invalid characters are counted - strtod() will find the valid + * number if it exists. The risk is that slightly more memory might + * be allocated before a parse error occurs. */ +static inline int valid_number_character(char ch) +{ + char lower_ch; + + if ('0' <= ch && ch <= '9') + return 1; + if (ch == '-' || ch == '+' || ch == '.') + return 1; + + /* Hex digits, exponent (e), base (p), "infinity",.. */ + lower_ch = ch | 0x20; + if ('a' <= lower_ch && lower_ch <= 'y') + return 1; + + return 0; +} + +/* Calculate the size of the buffer required for a strtod locale + * conversion. */ +static int strtod_buffer_size(const char *s) +{ + const char *p = s; + + while (valid_number_character(*p)) + p++; + + return p - s; +} + +/* Similar to strtod(), but must be passed the current locale's decimal point + * character. Guaranteed to be called at the start of any valid number in a string */ +double fpconv_strtod(const char *nptr, char **endptr) +{ + char localbuf[FPCONV_G_FMT_BUFSIZE]; + char *buf, *endbuf, *dp; + int buflen; + double value; + + /* System strtod() is fine when decimal point is '.' */ + if (locale_decimal_point == '.') + return strtod(nptr, endptr); + + buflen = strtod_buffer_size(nptr); + if (!buflen) { + /* No valid characters found, standard strtod() return */ + *endptr = (char *)nptr; + return 0; + } + + /* Duplicate number into buffer */ + if (buflen >= FPCONV_G_FMT_BUFSIZE) { + /* Handle unusually large numbers */ + buf = (char *)malloc(buflen + 1); + if (!buf) { + fprintf(stderr, "Out of memory"); + abort(); + } + } else { + /* This is the common case.. */ + buf = localbuf; + } + memcpy(buf, nptr, buflen); + buf[buflen] = 0; + + /* Update decimal point character if found */ + dp = strchr(buf, '.'); + if (dp) + *dp = locale_decimal_point; + + value = strtod(buf, &endbuf); + *endptr = (char *)&nptr[endbuf - buf]; + if (buflen >= FPCONV_G_FMT_BUFSIZE) + free(buf); + + return value; +} + +/* "fmt" must point to a buffer of at least 6 characters */ +static void set_number_format(char *fmt, int precision) +{ + int d1, d2, i; + + assert(1 <= precision && precision <= 16); + + /* Create printf format (%.14g) from precision */ + d1 = precision / 10; + d2 = precision % 10; + fmt[0] = '%'; + fmt[1] = '.'; + i = 2; + if (d1) { + fmt[i++] = '0' + d1; + } + fmt[i++] = '0' + d2; + fmt[i++] = 'g'; + fmt[i] = 0; +} + +/* Assumes there is always at least 32 characters available in the target buffer */ +int fpconv_g_fmt(char *str, double num, int precision) +{ + char buf[FPCONV_G_FMT_BUFSIZE]; + char fmt[6]; + int len; + char *b; + + set_number_format(fmt, precision); + + /* Pass through when decimal point character is dot. */ + if (locale_decimal_point == '.') + return snprintf(str, FPCONV_G_FMT_BUFSIZE, fmt, num); + + /* snprintf() to a buffer then translate for other decimal point characters */ + len = snprintf(buf, FPCONV_G_FMT_BUFSIZE, fmt, num); + + /* Copy into target location. Translate decimal point if required */ + b = buf; + do { + *str++ = (*b == locale_decimal_point ? '.' : *b); + } while(*b++); + + return len; +} + +void fpconv_init() +{ + fpconv_update_locale(); +} + +/* vi:ai et sw=4 ts=4: + */ diff --git a/framework/lualib-src/lua-cjson/fpconv.h b/framework/lualib-src/lua-cjson/fpconv.h new file mode 100644 index 0000000..7b0d0ee --- /dev/null +++ b/framework/lualib-src/lua-cjson/fpconv.h @@ -0,0 +1,22 @@ +/* Lua CJSON floating point conversion routines */ + +/* Buffer required to store the largest string representation of a double. + * + * Longest double printed with %.14g is 21 characters long: + * -1.7976931348623e+308 */ +# define FPCONV_G_FMT_BUFSIZE 32 + +#ifdef USE_INTERNAL_FPCONV +static inline void fpconv_init() +{ + /* Do nothing - not required */ +} +#else +extern void fpconv_init(); +#endif + +extern int fpconv_g_fmt(char*, double, int); +extern double fpconv_strtod(const char*, char**); + +/* vi:ai et sw=4 ts=4: + */ diff --git a/framework/lualib-src/lua-cjson/lua_cjson.c b/framework/lualib-src/lua-cjson/lua_cjson.c new file mode 100644 index 0000000..47462ad --- /dev/null +++ b/framework/lualib-src/lua-cjson/lua_cjson.c @@ -0,0 +1,1480 @@ +/* Lua CJSON - JSON support for Lua + * + * Copyright (c) 2010-2012 Mark Pulford + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* Caveats: + * - JSON "null" values are represented as lightuserdata since Lua + * tables cannot contain "nil". Compare with cjson.null. + * - Invalid UTF-8 characters are not detected and will be passed + * untouched. If required, UTF-8 error checking should be done + * outside this library. + * - Javascript comments are not part of the JSON spec, and are not + * currently supported. + * + * Note: Decoding is slower than encoding. Lua spends significant + * time (30%) managing tables when parsing JSON since it is + * difficult to know object/array sizes ahead of time. + */ + +#include +#include +#include +#include +#include +#include + +#include "strbuf.h" +#include "fpconv.h" + +#ifndef CJSON_MODNAME +#define CJSON_MODNAME "cjson" +#endif + +#ifndef CJSON_VERSION +#define CJSON_VERSION "2.1devel" +#endif + +#ifdef _MSC_VER +#define CJSON_EXPORT __declspec(dllexport) +#else +#define CJSON_EXPORT extern +#endif + +/* Workaround for Solaris platforms missing isinf() */ +#if !defined(isinf) && (defined(USE_INTERNAL_ISINF) || defined(MISSING_ISINF)) +#define isinf(x) (!isnan(x) && isnan((x) - (x))) +#endif + +#define DEFAULT_SPARSE_CONVERT 0 +#define DEFAULT_SPARSE_RATIO 2 +#define DEFAULT_SPARSE_SAFE 10 +#define DEFAULT_ENCODE_MAX_DEPTH 1000 +#define DEFAULT_DECODE_MAX_DEPTH 1000 +#define DEFAULT_ENCODE_INVALID_NUMBERS 0 +#define DEFAULT_DECODE_INVALID_NUMBERS 1 +#define DEFAULT_ENCODE_KEEP_BUFFER 1 +#define DEFAULT_ENCODE_NUMBER_PRECISION 14 +#define DEFAULT_ENCODE_EMPTY_TABLE_AS_ARRAY 0 + +#ifdef DISABLE_INVALID_NUMBERS +#undef DEFAULT_DECODE_INVALID_NUMBERS +#define DEFAULT_DECODE_INVALID_NUMBERS 0 +#endif + +typedef enum { + T_OBJ_BEGIN, + T_OBJ_END, + T_ARR_BEGIN, + T_ARR_END, + T_STRING, + T_NUMBER, + T_INTEGER, + T_BOOLEAN, + T_NULL, + T_COLON, + T_COMMA, + T_END, + T_WHITESPACE, + T_ERROR, + T_UNKNOWN +} json_token_type_t; + +static const char *json_token_type_name[] = { + "T_OBJ_BEGIN", + "T_OBJ_END", + "T_ARR_BEGIN", + "T_ARR_END", + "T_STRING", + "T_NUMBER", + "T_INTEGER", + "T_BOOLEAN", + "T_NULL", + "T_COLON", + "T_COMMA", + "T_END", + "T_WHITESPACE", + "T_ERROR", + "T_UNKNOWN", + NULL +}; + +typedef struct { + json_token_type_t ch2token[256]; + char escape2char[256]; /* Decoding */ + + /* encode_buf is only allocated and used when + * encode_keep_buffer is set */ + strbuf_t encode_buf; + + int encode_sparse_convert; + int encode_sparse_ratio; + int encode_sparse_safe; + int encode_max_depth; + int encode_invalid_numbers; /* 2 => Encode as "null" */ + int encode_number_precision; + int encode_keep_buffer; + int encode_empty_table_as_array; + + int decode_invalid_numbers; + int decode_max_depth; +} json_config_t; + +typedef struct { + const char *data; + const char *ptr; + strbuf_t *tmp; /* Temporary storage for strings */ + json_config_t *cfg; + int current_depth; +} json_parse_t; + +typedef struct { + json_token_type_t type; + int index; + union { + const char *string; + double number; + lua_Integer integer; + int boolean; + } value; + int string_len; +} json_token_t; + +static const char *char2escape[256] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", + "\\u0004", "\\u0005", "\\u0006", "\\u0007", + "\\b", "\\t", "\\n", "\\u000b", + "\\f", "\\r", "\\u000e", "\\u000f", + "\\u0010", "\\u0011", "\\u0012", "\\u0013", + "\\u0014", "\\u0015", "\\u0016", "\\u0017", + "\\u0018", "\\u0019", "\\u001a", "\\u001b", + "\\u001c", "\\u001d", "\\u001e", "\\u001f", + NULL, NULL, "\\\"", NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\/", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, "\\\\", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\u007f", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +}; + +/* ===== CONFIGURATION ===== */ + +static json_config_t *json_fetch_config(lua_State *l) +{ + json_config_t *cfg; + + cfg = (json_config_t *)lua_touserdata(l, lua_upvalueindex(1)); + if (!cfg) + luaL_error(l, "BUG: Unable to fetch CJSON configuration"); + + return cfg; +} + +/* Ensure the correct number of arguments have been provided. + * Pad with nil to allow other functions to simply check arg[i] + * to find whether an argument was provided */ +static json_config_t *json_arg_init(lua_State *l, int args) +{ + luaL_argcheck(l, lua_gettop(l) <= args, args + 1, + "found too many arguments"); + + while (lua_gettop(l) < args) + lua_pushnil(l); + + return json_fetch_config(l); +} + +/* Process integer options for configuration functions */ +static int json_integer_option(lua_State *l, int optindex, int *setting, + int min, int max) +{ + char errmsg[64]; + int value; + + if (!lua_isnil(l, optindex)) { + value = luaL_checkinteger(l, optindex); + snprintf(errmsg, sizeof(errmsg), "expected integer between %d and %d", min, max); + luaL_argcheck(l, min <= value && value <= max, 1, errmsg); + *setting = value; + } + + lua_pushinteger(l, *setting); + + return 1; +} + +/* Process enumerated arguments for a configuration function */ +static int json_enum_option(lua_State *l, int optindex, int *setting, + const char **options, int bool_true) +{ + static const char *bool_options[] = { "off", "on", NULL }; + + if (!options) { + options = bool_options; + bool_true = 1; + } + + if (!lua_isnil(l, optindex)) { + if (bool_true && lua_isboolean(l, optindex)) + *setting = lua_toboolean(l, optindex) * bool_true; + else + *setting = luaL_checkoption(l, optindex, NULL, options); + } + + if (bool_true && (*setting == 0 || *setting == bool_true)) + lua_pushboolean(l, *setting); + else + lua_pushstring(l, options[*setting]); + + return 1; +} + +/* Configures handling of extremely sparse arrays: + * convert: Convert extremely sparse arrays into objects? Otherwise error. + * ratio: 0: always allow sparse; 1: never allow sparse; >1: use ratio + * safe: Always use an array when the max index <= safe */ +static int json_cfg_encode_sparse_array(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 3); + + json_enum_option(l, 1, &cfg->encode_sparse_convert, NULL, 1); + json_integer_option(l, 2, &cfg->encode_sparse_ratio, 0, INT_MAX); + json_integer_option(l, 3, &cfg->encode_sparse_safe, 0, INT_MAX); + + return 3; +} + +/* Configures the maximum number of nested arrays/objects allowed when + * encoding */ +static int json_cfg_encode_max_depth(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + return json_integer_option(l, 1, &cfg->encode_max_depth, 1, INT_MAX); +} + +/* Configures the maximum number of nested arrays/objects allowed when + * encoding */ +static int json_cfg_decode_max_depth(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + return json_integer_option(l, 1, &cfg->decode_max_depth, 1, INT_MAX); +} + +/* Configures number precision when converting doubles to text */ +static int json_cfg_encode_number_precision(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + return json_integer_option(l, 1, &cfg->encode_number_precision, 1, 16); +} + +/* Configures JSON encoding buffer persistence */ +static int json_cfg_encode_keep_buffer(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + int old_value; + + old_value = cfg->encode_keep_buffer; + + json_enum_option(l, 1, &cfg->encode_keep_buffer, NULL, 1); + + /* Init / free the buffer if the setting has changed */ + if (old_value ^ cfg->encode_keep_buffer) { + if (cfg->encode_keep_buffer) + strbuf_init(&cfg->encode_buf, 0); + else + strbuf_free(&cfg->encode_buf); + } + + return 1; +} + +#if defined(DISABLE_INVALID_NUMBERS) && !defined(USE_INTERNAL_FPCONV) +void json_verify_invalid_number_setting(lua_State *l, int *setting) +{ + if (*setting == 1) { + *setting = 0; + luaL_error(l, "Infinity, NaN, and/or hexadecimal numbers are not supported."); + } +} +#else +#define json_verify_invalid_number_setting(l, s) do { } while(0) +#endif + +static int json_cfg_encode_invalid_numbers(lua_State *l) +{ + static const char *options[] = { "off", "on", "null", NULL }; + json_config_t *cfg = json_arg_init(l, 1); + + json_enum_option(l, 1, &cfg->encode_invalid_numbers, options, 1); + + json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers); + + return 1; +} + +static int json_cfg_decode_invalid_numbers(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + json_enum_option(l, 1, &cfg->decode_invalid_numbers, NULL, 1); + + json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers); + + return 1; +} + +static int json_cfg_encode_empty_tables_as_array(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + return json_enum_option(l, 1, &cfg->encode_empty_table_as_array, NULL, 1); +} + +static int json_destroy_config(lua_State *l) +{ + json_config_t *cfg; + + cfg = (json_config_t *)lua_touserdata(l, 1); + if (cfg) + strbuf_free(&cfg->encode_buf); + cfg = NULL; + + return 0; +} + +static void json_create_config(lua_State *l) +{ + json_config_t *cfg; + int i; + + cfg = (json_config_t *)lua_newuserdata(l, sizeof(*cfg)); + + /* Create GC method to clean up strbuf */ + lua_newtable(l); + lua_pushcfunction(l, json_destroy_config); + lua_setfield(l, -2, "__gc"); + lua_setmetatable(l, -2); + + cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT; + cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO; + cfg->encode_sparse_safe = DEFAULT_SPARSE_SAFE; + cfg->encode_max_depth = DEFAULT_ENCODE_MAX_DEPTH; + cfg->decode_max_depth = DEFAULT_DECODE_MAX_DEPTH; + cfg->encode_invalid_numbers = DEFAULT_ENCODE_INVALID_NUMBERS; + cfg->decode_invalid_numbers = DEFAULT_DECODE_INVALID_NUMBERS; + cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; + cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION; + cfg->encode_empty_table_as_array = DEFAULT_ENCODE_EMPTY_TABLE_AS_ARRAY; + +#if DEFAULT_ENCODE_KEEP_BUFFER > 0 + strbuf_init(&cfg->encode_buf, 0); +#endif + + /* Decoding init */ + + /* Tag all characters as an error */ + for (i = 0; i < 256; i++) + cfg->ch2token[i] = T_ERROR; + + /* Set tokens that require no further processing */ + cfg->ch2token['{'] = T_OBJ_BEGIN; + cfg->ch2token['}'] = T_OBJ_END; + cfg->ch2token['['] = T_ARR_BEGIN; + cfg->ch2token[']'] = T_ARR_END; + cfg->ch2token[','] = T_COMMA; + cfg->ch2token[':'] = T_COLON; + cfg->ch2token['\0'] = T_END; + cfg->ch2token[' '] = T_WHITESPACE; + cfg->ch2token['\t'] = T_WHITESPACE; + cfg->ch2token['\n'] = T_WHITESPACE; + cfg->ch2token['\r'] = T_WHITESPACE; + + /* Update characters that require further processing */ + cfg->ch2token['f'] = T_UNKNOWN; /* false? */ + cfg->ch2token['i'] = T_UNKNOWN; /* inf, ininity? */ + cfg->ch2token['I'] = T_UNKNOWN; + cfg->ch2token['n'] = T_UNKNOWN; /* null, nan? */ + cfg->ch2token['N'] = T_UNKNOWN; + cfg->ch2token['t'] = T_UNKNOWN; /* true? */ + cfg->ch2token['"'] = T_UNKNOWN; /* string? */ + cfg->ch2token['+'] = T_UNKNOWN; /* number? */ + cfg->ch2token['-'] = T_UNKNOWN; + for (i = 0; i < 10; i++) + cfg->ch2token['0' + i] = T_UNKNOWN; + + /* Lookup table for parsing escape characters */ + for (i = 0; i < 256; i++) + cfg->escape2char[i] = 0; /* String error */ + cfg->escape2char['"'] = '"'; + cfg->escape2char['\\'] = '\\'; + cfg->escape2char['/'] = '/'; + cfg->escape2char['b'] = '\b'; + cfg->escape2char['t'] = '\t'; + cfg->escape2char['n'] = '\n'; + cfg->escape2char['f'] = '\f'; + cfg->escape2char['r'] = '\r'; + cfg->escape2char['u'] = 'u'; /* Unicode parsing required */ +} + +/* ===== ENCODING ===== */ + +static void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *json, int lindex, + const char *reason) +{ + if (!cfg->encode_keep_buffer) + strbuf_free(json); + luaL_error(l, "Cannot serialise %s: %s", + lua_typename(l, lua_type(l, lindex)), reason); +} + +/* json_append_string args: + * - lua_State + * - JSON strbuf + * - String (Lua stack index) + * + * Returns nothing. Doesn't remove string from Lua stack */ +static void json_append_string(lua_State *l, strbuf_t *json, int lindex) +{ + const char *escstr; + const char *str; + size_t len; + size_t i; + + str = lua_tolstring(l, lindex, &len); + + /* Worst case is len * 6 (all unicode escapes). + * This buffer is reused constantly for small strings + * If there are any excess pages, they won't be hit anyway. + * This gains ~5% speedup. */ + strbuf_ensure_empty_length(json, len * 6 + 2); + + strbuf_append_char_unsafe(json, '\"'); + for (i = 0; i < len; i++) { + escstr = char2escape[(unsigned char)str[i]]; + if (escstr) + strbuf_append_string(json, escstr); + else + strbuf_append_char_unsafe(json, str[i]); + } + strbuf_append_char_unsafe(json, '\"'); +} + +/* Find the size of the array on the top of the Lua stack + * -1 object (not a pure array) + * >=0 elements in array + */ +static int lua_array_length(lua_State *l, json_config_t *cfg, strbuf_t *json) +{ + double k; + int max; + int items; + + max = 0; + items = 0; + + lua_pushnil(l); + /* table, startkey */ + while (lua_next(l, -2) != 0) { + /* table, key, value */ + if (lua_type(l, -2) == LUA_TNUMBER && + (k = lua_tonumber(l, -2))) { + /* Integer >= 1 ? */ + if (floor(k) == k && k >= 1) { + if (k > max) + max = k; + items++; + lua_pop(l, 1); + continue; + } + } + + /* Must not be an array (non integer key) */ + lua_pop(l, 2); + return -1; + } + + /* Encode excessively sparse arrays as objects (if enabled) */ + if (cfg->encode_sparse_ratio > 0 && + max > items * cfg->encode_sparse_ratio && + max > cfg->encode_sparse_safe) { + if (!cfg->encode_sparse_convert) + json_encode_exception(l, cfg, json, -1, "excessively sparse array"); + + return -1; + } + + return max; +} + +static void json_check_encode_depth(lua_State *l, json_config_t *cfg, + int current_depth, strbuf_t *json) +{ + /* Ensure there are enough slots free to traverse a table (key, + * value) and push a string for a potential error message. + * + * Unlike "decode", the key and value are still on the stack when + * lua_checkstack() is called. Hence an extra slot for luaL_error() + * below is required just in case the next check to lua_checkstack() + * fails. + * + * While this won't cause a crash due to the EXTRA_STACK reserve + * slots, it would still be an improper use of the API. */ + if (current_depth <= cfg->encode_max_depth && lua_checkstack(l, 3)) + return; + + if (!cfg->encode_keep_buffer) + strbuf_free(json); + + luaL_error(l, "Cannot serialise, excessive nesting (%d)", + current_depth); +} + +static void json_append_data(lua_State *l, json_config_t *cfg, + int current_depth, strbuf_t *json); + +/* json_append_array args: + * - lua_State + * - JSON strbuf + * - Size of passwd Lua array (top of stack) */ +static void json_append_array(lua_State *l, json_config_t *cfg, int current_depth, + strbuf_t *json, int array_length) +{ + int comma, i; + + strbuf_append_char(json, '['); + + comma = 0; + for (i = 1; i <= array_length; i++) { + if (comma) + strbuf_append_char(json, ','); + else + comma = 1; + + lua_geti(l, -1, i); + json_append_data(l, cfg, current_depth, json); + lua_pop(l, 1); + } + + strbuf_append_char(json, ']'); +} + +static void json_append_number(lua_State *l, json_config_t *cfg, + strbuf_t *json, int lindex) +{ + int len; +#if LUA_VERSION_NUM >= 503 + if (lua_isinteger(l, lindex)) { + lua_Integer num = lua_tointeger(l, lindex); + strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE); /* max length of int64 is 19 */ + len = sprintf(strbuf_empty_ptr(json), LUA_INTEGER_FMT, num); + strbuf_extend_length(json, len); + return; + } +#endif + double num = lua_tonumber(l, lindex); + + if (cfg->encode_invalid_numbers == 0) { + /* Prevent encoding invalid numbers */ + if (isinf(num) || isnan(num)) + json_encode_exception(l, cfg, json, lindex, + "must not be NaN or Infinity"); + } else if (cfg->encode_invalid_numbers == 1) { + /* Encode NaN/Infinity separately to ensure Javascript compatible + * values are used. */ + if (isnan(num)) { + strbuf_append_mem(json, "NaN", 3); + return; + } + if (isinf(num)) { + if (num < 0) + strbuf_append_mem(json, "-Infinity", 9); + else + strbuf_append_mem(json, "Infinity", 8); + return; + } + } else { + /* Encode invalid numbers as "null" */ + if (isinf(num) || isnan(num)) { + strbuf_append_mem(json, "null", 4); + return; + } + } + + strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE); + len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision); + strbuf_extend_length(json, len); +} + +static void json_append_object(lua_State *l, json_config_t *cfg, + int current_depth, strbuf_t *json) +{ + int comma, keytype; + + /* Object */ + strbuf_append_char(json, '{'); + + lua_pushnil(l); + /* table, startkey */ + comma = 0; + while (lua_next(l, -2) != 0) { + if (comma) + strbuf_append_char(json, ','); + else + comma = 1; + + /* table, key, value */ + keytype = lua_type(l, -2); + if (keytype == LUA_TNUMBER) { + strbuf_append_char(json, '"'); + json_append_number(l, cfg, json, -2); + strbuf_append_mem(json, "\":", 2); + } else if (keytype == LUA_TSTRING) { + json_append_string(l, json, -2); + strbuf_append_char(json, ':'); + } else { + json_encode_exception(l, cfg, json, -2, + "table key must be a number or string"); + /* never returns */ + } + + /* table, key, value */ + json_append_data(l, cfg, current_depth, json); + lua_pop(l, 1); + /* table, key */ + } + + strbuf_append_char(json, '}'); +} + +/* Serialise Lua data into JSON string. */ +static void json_append_data(lua_State *l, json_config_t *cfg, + int current_depth, strbuf_t *json) +{ + int len; + + switch (lua_type(l, -1)) { + case LUA_TSTRING: + json_append_string(l, json, -1); + break; + case LUA_TNUMBER: + json_append_number(l, cfg, json, -1); + break; + case LUA_TBOOLEAN: + if (lua_toboolean(l, -1)) + strbuf_append_mem(json, "true", 4); + else + strbuf_append_mem(json, "false", 5); + break; + case LUA_TTABLE: + current_depth++; + json_check_encode_depth(l, cfg, current_depth, json); + if (luaL_getmetafield(l, -1, "__len") != LUA_TNIL) { + lua_pushvalue(l, -2); + lua_call(l, 1, 1); + if (!lua_isinteger(l, -1)) { + luaL_error(l, "__len should return integer"); + } + len = lua_tointeger(l, -1); + lua_pop(l, 1); + json_append_array(l, cfg, current_depth, json, len); + } else { + len = lua_array_length(l, cfg, json); + if (len > 0 || (cfg->encode_empty_table_as_array && len == 0)) + json_append_array(l, cfg, current_depth, json, len); + else + json_append_object(l, cfg, current_depth, json); + } + break; + case LUA_TNIL: + strbuf_append_mem(json, "null", 4); + break; + case LUA_TLIGHTUSERDATA: + if (lua_touserdata(l, -1) == NULL) { + strbuf_append_mem(json, "null", 4); + break; + } + default: + /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, + * and LUA_TLIGHTUSERDATA) cannot be serialised */ + json_encode_exception(l, cfg, json, -1, "type not supported"); + /* never returns */ + } +} + +static int json_encode(lua_State *l) +{ + json_config_t *cfg = json_fetch_config(l); + strbuf_t local_encode_buf; + strbuf_t *encode_buf; + char *json; + int len; + + luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); + + if (!cfg->encode_keep_buffer) { + /* Use private buffer */ + encode_buf = &local_encode_buf; + strbuf_init(encode_buf, 0); + } else { + /* Reuse existing buffer */ + encode_buf = &cfg->encode_buf; + strbuf_reset(encode_buf); + } + + json_append_data(l, cfg, 0, encode_buf); + json = strbuf_string(encode_buf, &len); + + lua_pushlstring(l, json, len); + + if (!cfg->encode_keep_buffer) + strbuf_free(encode_buf); + + return 1; +} + +/* ===== DECODING ===== */ + +static void json_process_value(lua_State *l, json_parse_t *json, + json_token_t *token); + +static int hexdigit2int(char hex) +{ + if ('0' <= hex && hex <= '9') + return hex - '0'; + + /* Force lowercase */ + hex |= 0x20; + if ('a' <= hex && hex <= 'f') + return 10 + hex - 'a'; + + return -1; +} + +static int decode_hex4(const char *hex) +{ + int digit[4]; + int i; + + /* Convert ASCII hex digit to numeric digit + * Note: this returns an error for invalid hex digits, including + * NULL */ + for (i = 0; i < 4; i++) { + digit[i] = hexdigit2int(hex[i]); + if (digit[i] < 0) { + return -1; + } + } + + return (digit[0] << 12) + + (digit[1] << 8) + + (digit[2] << 4) + + digit[3]; +} + +/* Converts a Unicode codepoint to UTF-8. + * Returns UTF-8 string length, and up to 4 bytes in *utf8 */ +static int codepoint_to_utf8(char *utf8, int codepoint) +{ + /* 0xxxxxxx */ + if (codepoint <= 0x7F) { + utf8[0] = codepoint; + return 1; + } + + /* 110xxxxx 10xxxxxx */ + if (codepoint <= 0x7FF) { + utf8[0] = (codepoint >> 6) | 0xC0; + utf8[1] = (codepoint & 0x3F) | 0x80; + return 2; + } + + /* 1110xxxx 10xxxxxx 10xxxxxx */ + if (codepoint <= 0xFFFF) { + utf8[0] = (codepoint >> 12) | 0xE0; + utf8[1] = ((codepoint >> 6) & 0x3F) | 0x80; + utf8[2] = (codepoint & 0x3F) | 0x80; + return 3; + } + + /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint <= 0x1FFFFF) { + utf8[0] = (codepoint >> 18) | 0xF0; + utf8[1] = ((codepoint >> 12) & 0x3F) | 0x80; + utf8[2] = ((codepoint >> 6) & 0x3F) | 0x80; + utf8[3] = (codepoint & 0x3F) | 0x80; + return 4; + } + + return 0; +} + + +/* Called when index pointing to beginning of UTF-16 code escape: \uXXXX + * \u is guaranteed to exist, but the remaining hex characters may be + * missing. + * Translate to UTF-8 and append to temporary token string. + * Must advance index to the next character to be processed. + * Returns: 0 success + * -1 error + */ +static int json_append_unicode_escape(json_parse_t *json) +{ + char utf8[4]; /* Surrogate pairs require 4 UTF-8 bytes */ + int codepoint; + int surrogate_low; + int len; + int escape_len = 6; + + /* Fetch UTF-16 code unit */ + codepoint = decode_hex4(json->ptr + 2); + if (codepoint < 0) + return -1; + + /* UTF-16 surrogate pairs take the following 2 byte form: + * 11011 x yyyyyyyyyy + * When x = 0: y is the high 10 bits of the codepoint + * x = 1: y is the low 10 bits of the codepoint + * + * Check for a surrogate pair (high or low) */ + if ((codepoint & 0xF800) == 0xD800) { + /* Error if the 1st surrogate is not high */ + if (codepoint & 0x400) + return -1; + + /* Ensure the next code is a unicode escape */ + if (*(json->ptr + escape_len) != '\\' || + *(json->ptr + escape_len + 1) != 'u') { + return -1; + } + + /* Fetch the next codepoint */ + surrogate_low = decode_hex4(json->ptr + 2 + escape_len); + if (surrogate_low < 0) + return -1; + + /* Error if the 2nd code is not a low surrogate */ + if ((surrogate_low & 0xFC00) != 0xDC00) + return -1; + + /* Calculate Unicode codepoint */ + codepoint = (codepoint & 0x3FF) << 10; + surrogate_low &= 0x3FF; + codepoint = (codepoint | surrogate_low) + 0x10000; + escape_len = 12; + } + + /* Convert codepoint to UTF-8 */ + len = codepoint_to_utf8(utf8, codepoint); + if (!len) + return -1; + + /* Append bytes and advance parse index */ + strbuf_append_mem_unsafe(json->tmp, utf8, len); + json->ptr += escape_len; + + return 0; +} + +static void json_set_token_error(json_token_t *token, json_parse_t *json, + const char *errtype) +{ + token->type = T_ERROR; + token->index = json->ptr - json->data; + token->value.string = errtype; +} + +static void json_next_string_token(json_parse_t *json, json_token_t *token) +{ + char *escape2char = json->cfg->escape2char; + char ch; + + /* Caller must ensure a string is next */ + assert(*json->ptr == '"'); + + /* Skip " */ + json->ptr++; + + /* json->tmp is the temporary strbuf used to accumulate the + * decoded string value. + * json->tmp is sized to handle JSON containing only a string value. + */ + strbuf_reset(json->tmp); + + while ((ch = *json->ptr) != '"') { + if (!ch) { + /* Premature end of the string */ + json_set_token_error(token, json, "unexpected end of string"); + return; + } + + /* Handle escapes */ + if (ch == '\\') { + /* Fetch escape character */ + ch = *(json->ptr + 1); + + /* Translate escape code and append to tmp string */ + ch = escape2char[(unsigned char)ch]; + if (ch == 'u') { + if (json_append_unicode_escape(json) == 0) + continue; + + json_set_token_error(token, json, + "invalid unicode escape code"); + return; + } + if (!ch) { + json_set_token_error(token, json, "invalid escape code"); + return; + } + + /* Skip '\' */ + json->ptr++; + } + /* Append normal character or translated single character + * Unicode escapes are handled above */ + strbuf_append_char_unsafe(json->tmp, ch); + json->ptr++; + } + json->ptr++; /* Eat final quote (") */ + + strbuf_ensure_null(json->tmp); + + token->type = T_STRING; + token->value.string = strbuf_string(json->tmp, &token->string_len); +} + +/* JSON numbers should take the following form: + * -?(0|[1-9]|[1-9][0-9]+)(.[0-9]+)?([eE][-+]?[0-9]+)? + * + * json_next_number_token() uses strtod() which allows other forms: + * - numbers starting with '+' + * - NaN, -NaN, infinity, -infinity + * - hexadecimal numbers + * - numbers with leading zeros + * + * json_is_invalid_number() detects "numbers" which may pass strtod()'s + * error checking, but should not be allowed with strict JSON. + * + * json_is_invalid_number() may pass numbers which cause strtod() + * to generate an error. + */ +static int json_is_invalid_number(json_parse_t *json) +{ + const char *p = json->ptr; + + /* Reject numbers starting with + */ + if (*p == '+') + return 1; + + /* Skip minus sign if it exists */ + if (*p == '-') + p++; + + /* Reject numbers starting with 0x, or leading zeros */ + if (*p == '0') { + int ch2 = *(p + 1); + + if ((ch2 | 0x20) == 'x' || /* Hex */ + ('0' <= ch2 && ch2 <= '9')) /* Leading zero */ + return 1; + + return 0; + } else if (*p <= '9') { + return 0; /* Ordinary number */ + } + + /* Reject inf/nan */ + if (!strncasecmp(p, "inf", 3)) + return 1; + if (!strncasecmp(p, "nan", 3)) + return 1; + + /* Pass all other numbers which may still be invalid, but + * strtod() will catch them. */ + return 0; +} + +static void json_next_number_token(json_parse_t *json, json_token_t *token) +{ + char *endptr; + token->value.integer = strtoll(json->ptr, &endptr, 0); + if (json->ptr == endptr) { + json_set_token_error(token, json, "invalid number"); + return; + } + if (*endptr == '.' || *endptr == 'e' || *endptr == 'E') { + token->type = T_NUMBER; + token->value.number = fpconv_strtod(json->ptr, &endptr); + } else { + token->type = T_INTEGER; + } + json->ptr = endptr; /* Skip the processed number */ + + return; +} + +/* Fills in the token struct. + * T_STRING will return a pointer to the json_parse_t temporary string + * T_ERROR will leave the json->ptr pointer at the error. + */ +static void json_next_token(json_parse_t *json, json_token_t *token) +{ + const json_token_type_t *ch2token = json->cfg->ch2token; + int ch; + + /* Eat whitespace. */ + while (1) { + ch = (unsigned char)*(json->ptr); + token->type = ch2token[ch]; + if (token->type != T_WHITESPACE) + break; + json->ptr++; + } + + /* Store location of new token. Required when throwing errors + * for unexpected tokens (syntax errors). */ + token->index = json->ptr - json->data; + + /* Don't advance the pointer for an error or the end */ + if (token->type == T_ERROR) { + json_set_token_error(token, json, "invalid token"); + return; + } + + if (token->type == T_END) { + return; + } + + /* Found a known single character token, advance index and return */ + if (token->type != T_UNKNOWN) { + json->ptr++; + return; + } + + /* Process characters which triggered T_UNKNOWN + * + * Must use strncmp() to match the front of the JSON string. + * JSON identifier must be lowercase. + * When strict_numbers if disabled, either case is allowed for + * Infinity/NaN (since we are no longer following the spec..) */ + if (ch == '"') { + json_next_string_token(json, token); + return; + } else if (ch == '-' || ('0' <= ch && ch <= '9')) { + if (!json->cfg->decode_invalid_numbers && json_is_invalid_number(json)) { + json_set_token_error(token, json, "invalid number"); + return; + } + json_next_number_token(json, token); + return; + } else if (!strncmp(json->ptr, "true", 4)) { + token->type = T_BOOLEAN; + token->value.boolean = 1; + json->ptr += 4; + return; + } else if (!strncmp(json->ptr, "false", 5)) { + token->type = T_BOOLEAN; + token->value.boolean = 0; + json->ptr += 5; + return; + } else if (!strncmp(json->ptr, "null", 4)) { + token->type = T_NULL; + json->ptr += 4; + return; + } else if (json->cfg->decode_invalid_numbers && + json_is_invalid_number(json)) { + /* When decode_invalid_numbers is enabled, only attempt to process + * numbers we know are invalid JSON (Inf, NaN, hex) + * This is required to generate an appropriate token error, + * otherwise all bad tokens will register as "invalid number" + */ + json_next_number_token(json, token); + return; + } + + /* Token starts with t/f/n but isn't recognised above. */ + json_set_token_error(token, json, "invalid token"); +} + +/* This function does not return. + * DO NOT CALL WITH DYNAMIC MEMORY ALLOCATED. + * The only supported exception is the temporary parser string + * json->tmp struct. + * json and token should exist on the stack somewhere. + * luaL_error() will long_jmp and release the stack */ +static void json_throw_parse_error(lua_State *l, json_parse_t *json, + const char *exp, json_token_t *token) +{ + const char *found; + + strbuf_free(json->tmp); + + if (token->type == T_ERROR) + found = token->value.string; + else + found = json_token_type_name[token->type]; + + /* Note: token->index is 0 based, display starting from 1 */ + luaL_error(l, "Expected %s but found %s at character %d", + exp, found, token->index + 1); +} + +static inline void json_decode_ascend(json_parse_t *json) +{ + json->current_depth--; +} + +static void json_decode_descend(lua_State *l, json_parse_t *json, int slots) +{ + json->current_depth++; + + if (json->current_depth <= json->cfg->decode_max_depth && + lua_checkstack(l, slots)) { + return; + } + + strbuf_free(json->tmp); + luaL_error(l, "Found too many nested data structures (%d) at character %d", + json->current_depth, json->ptr - json->data); +} + +static void json_parse_object_context(lua_State *l, json_parse_t *json) +{ + json_token_t token; + + /* 3 slots required: + * .., table, key, value */ + json_decode_descend(l, json, 3); + + lua_newtable(l); + + json_next_token(json, &token); + + /* Handle empty objects */ + if (token.type == T_OBJ_END) { + json_decode_ascend(json); + return; + } + + while (1) { + if (token.type != T_STRING) + json_throw_parse_error(l, json, "object key string", &token); + + /* Push key */ + lua_pushlstring(l, token.value.string, token.string_len); + + json_next_token(json, &token); + if (token.type != T_COLON) + json_throw_parse_error(l, json, "colon", &token); + + /* Fetch value */ + json_next_token(json, &token); + json_process_value(l, json, &token); + + /* Set key = value */ + lua_rawset(l, -3); + + json_next_token(json, &token); + + if (token.type == T_OBJ_END) { + json_decode_ascend(json); + return; + } + + if (token.type != T_COMMA) + json_throw_parse_error(l, json, "comma or object end", &token); + + json_next_token(json, &token); + } +} + +/* Handle the array context */ +static void json_parse_array_context(lua_State *l, json_parse_t *json) +{ + json_token_t token; + int i; + + /* 2 slots required: + * .., table, value */ + json_decode_descend(l, json, 2); + + lua_newtable(l); + + json_next_token(json, &token); + + /* Handle empty arrays */ + if (token.type == T_ARR_END) { + json_decode_ascend(json); + return; + } + + for (i = 1; ; i++) { + json_process_value(l, json, &token); + lua_rawseti(l, -2, i); /* arr[i] = value */ + + json_next_token(json, &token); + + if (token.type == T_ARR_END) { + json_decode_ascend(json); + return; + } + + if (token.type != T_COMMA) + json_throw_parse_error(l, json, "comma or array end", &token); + + json_next_token(json, &token); + } +} + +/* Handle the "value" context */ +static void json_process_value(lua_State *l, json_parse_t *json, + json_token_t *token) +{ + switch (token->type) { + case T_STRING: + lua_pushlstring(l, token->value.string, token->string_len); + break;; + case T_NUMBER: + lua_pushnumber(l, token->value.number); + break;; + case T_INTEGER: + lua_pushinteger(l, token->value.integer); + break;; + case T_BOOLEAN: + lua_pushboolean(l, token->value.boolean); + break;; + case T_OBJ_BEGIN: + json_parse_object_context(l, json); + break;; + case T_ARR_BEGIN: + json_parse_array_context(l, json); + break;; + case T_NULL: + /* In Lua, setting "t[k] = nil" will delete k from the table. + * Hence a NULL pointer lightuserdata object is used instead */ + lua_pushlightuserdata(l, NULL); + break;; + default: + json_throw_parse_error(l, json, "value", token); + } +} + +static int json_decode(lua_State *l) +{ + json_parse_t json; + json_token_t token; + size_t json_len; + + luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); + + json.cfg = json_fetch_config(l); + json.data = luaL_checklstring(l, 1, &json_len); + json.current_depth = 0; + json.ptr = json.data; + + /* Detect Unicode other than UTF-8 (see RFC 4627, Sec 3) + * + * CJSON can support any simple data type, hence only the first + * character is guaranteed to be ASCII (at worst: '"'). This is + * still enough to detect whether the wrong encoding is in use. */ + if (json_len >= 2 && (!json.data[0] || !json.data[1])) + luaL_error(l, "JSON parser does not support UTF-16 or UTF-32"); + + /* Ensure the temporary buffer can hold the entire string. + * This means we no longer need to do length checks since the decoded + * string must be smaller than the entire json string */ + json.tmp = strbuf_new(json_len); + + json_next_token(&json, &token); + json_process_value(l, &json, &token); + + /* Ensure there is no more input left */ + json_next_token(&json, &token); + + if (token.type != T_END) + json_throw_parse_error(l, &json, "the end", &token); + + strbuf_free(json.tmp); + + return 1; +} + +/* ===== INITIALISATION ===== */ + +#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 +/* Compatibility for Lua 5.1. + * + * luaL_setfuncs() is used to create a module table where the functions have + * json_config_t as their first upvalue. Code borrowed from Lua 5.2 source. */ +static void luaL_setfuncs (lua_State *l, const luaL_Reg *reg, int nup) +{ + int i; + + luaL_checkstack(l, nup, "too many upvalues"); + for (; reg->name != NULL; reg++) { /* fill the table with given functions */ + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(l, -nup); + lua_pushcclosure(l, reg->func, nup); /* closure with those upvalues */ + lua_setfield(l, -(nup + 2), reg->name); + } + lua_pop(l, nup); /* remove upvalues */ +} +#endif + +/* Call target function in protected mode with all supplied args. + * Assumes target function only returns a single non-nil value. + * Convert and return thrown errors as: nil, "error message" */ +static int json_protect_conversion(lua_State *l) +{ + int err; + + /* Deliberately throw an error for invalid arguments */ + luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); + + /* pcall() the function stored as upvalue(1) */ + lua_pushvalue(l, lua_upvalueindex(1)); + lua_insert(l, 1); + err = lua_pcall(l, 1, 1, 0); + if (!err) + return 1; + + if (err == LUA_ERRRUN) { + lua_pushnil(l); + lua_insert(l, -2); + return 2; + } + + /* Since we are not using a custom error handler, the only remaining + * errors are memory related */ + return luaL_error(l, "Memory allocation error in CJSON protected call"); +} + +/* Return cjson module table */ +static int lua_cjson_new(lua_State *l) +{ + luaL_Reg reg[] = { + { "encode", json_encode }, + { "decode", json_decode }, + { "encode_sparse_array", json_cfg_encode_sparse_array }, + { "encode_max_depth", json_cfg_encode_max_depth }, + { "decode_max_depth", json_cfg_decode_max_depth }, + { "encode_number_precision", json_cfg_encode_number_precision }, + { "encode_keep_buffer", json_cfg_encode_keep_buffer }, + { "encode_invalid_numbers", json_cfg_encode_invalid_numbers }, + { "decode_invalid_numbers", json_cfg_decode_invalid_numbers }, + { "encode_empty_table_as_array", json_cfg_encode_empty_tables_as_array }, + { "new", lua_cjson_new }, + { NULL, NULL } + }; + + /* Initialise number conversions */ + fpconv_init(); + + /* cjson module table */ + lua_newtable(l); + + /* Register functions with config data as upvalue */ + json_create_config(l); + luaL_setfuncs(l, reg, 1); + + /* Set cjson.null */ + lua_pushlightuserdata(l, NULL); + lua_setfield(l, -2, "null"); + + /* Set module name / version fields */ + lua_pushliteral(l, CJSON_MODNAME); + lua_setfield(l, -2, "_NAME"); + lua_pushliteral(l, CJSON_VERSION); + lua_setfield(l, -2, "_VERSION"); + + return 1; +} + +/* Return cjson.safe module table */ +static int lua_cjson_safe_new(lua_State *l) +{ + const char *func[] = { "decode", "encode", NULL }; + int i; + + lua_cjson_new(l); + + /* Fix new() method */ + lua_pushcfunction(l, lua_cjson_safe_new); + lua_setfield(l, -2, "new"); + + for (i = 0; func[i]; i++) { + lua_getfield(l, -1, func[i]); + lua_pushcclosure(l, json_protect_conversion, 1); + lua_setfield(l, -2, func[i]); + } + + return 1; +} + +CJSON_EXPORT int luaopen_cjson(lua_State *l) +{ + lua_cjson_new(l); + +#ifdef ENABLE_CJSON_GLOBAL + /* Register a global "cjson" table. */ + lua_pushvalue(l, -1); + lua_setglobal(l, CJSON_MODNAME); +#endif + + /* Return cjson table */ + return 1; +} + +CJSON_EXPORT int luaopen_cjson_safe(lua_State *l) +{ + lua_cjson_safe_new(l); + + /* Return cjson.safe table */ + return 1; +} + +/* vi:ai et sw=4 ts=4: + */ diff --git a/framework/lualib-src/lua-cjson/strbuf.c b/framework/lualib-src/lua-cjson/strbuf.c new file mode 100644 index 0000000..ac779e4 --- /dev/null +++ b/framework/lualib-src/lua-cjson/strbuf.c @@ -0,0 +1,252 @@ +/* strbuf - String buffer routines + * + * Copyright (c) 2010-2012 Mark Pulford + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "strbuf.h" + +static void die(const char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + vfprintf(stderr, fmt, arg); + va_end(arg); + fprintf(stderr, "\n"); + + exit(-1); +} + +void strbuf_init(strbuf_t *s, int len) +{ + int size; + + if (len <= 0) + size = STRBUF_DEFAULT_SIZE; + else + size = len + 1; /* \0 terminator */ + + s->buf = NULL; + s->size = size; + s->length = 0; + s->increment = STRBUF_DEFAULT_INCREMENT; + s->dynamic = 0; + s->reallocs = 0; + s->debug = 0; + + s->buf = (char *)malloc(size); + if (!s->buf) + die("Out of memory"); + + strbuf_ensure_null(s); +} + +strbuf_t *strbuf_new(int len) +{ + strbuf_t *s; + + s = (strbuf_t*)malloc(sizeof(strbuf_t)); + if (!s) + die("Out of memory"); + + strbuf_init(s, len); + + /* Dynamic strbuf allocation / deallocation */ + s->dynamic = 1; + + return s; +} + +void strbuf_set_increment(strbuf_t *s, int increment) +{ + /* Increment > 0: Linear buffer growth rate + * Increment < -1: Exponential buffer growth rate */ + if (increment == 0 || increment == -1) + die("BUG: Invalid string increment"); + + s->increment = increment; +} + +static inline void debug_stats(strbuf_t *s) +{ + if (s->debug) { + fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n", + (long)s, s->reallocs, s->length, s->size); + } +} + +/* If strbuf_t has not been dynamically allocated, strbuf_free() can + * be called any number of times strbuf_init() */ +void strbuf_free(strbuf_t *s) +{ + debug_stats(s); + + if (s->buf) { + free(s->buf); + s->buf = NULL; + } + if (s->dynamic) + free(s); +} + +char *strbuf_free_to_string(strbuf_t *s, int *len) +{ + char *buf; + + debug_stats(s); + + strbuf_ensure_null(s); + + buf = s->buf; + if (len) + *len = s->length; + + if (s->dynamic) + free(s); + + return buf; +} + +static int calculate_new_size(strbuf_t *s, int len) +{ + int reqsize, newsize; + + if (len <= 0) + die("BUG: Invalid strbuf length requested"); + + /* Ensure there is room for optional NULL termination */ + reqsize = len + 1; + + /* If the user has requested to shrink the buffer, do it exactly */ + if (s->size > reqsize) + return reqsize; + + newsize = s->size; + if (s->increment < 0) { + /* Exponential sizing */ + while (newsize < reqsize) + newsize *= -s->increment; + } else { + /* Linear sizing */ + newsize = ((newsize + s->increment - 1) / s->increment) * s->increment; + } + + return newsize; +} + + +/* Ensure strbuf can handle a string length bytes long (ignoring NULL + * optional termination). */ +void strbuf_resize(strbuf_t *s, int len) +{ + int newsize; + + newsize = calculate_new_size(s, len); + + if (s->debug > 1) { + fprintf(stderr, "strbuf(%lx) resize: %d => %d\n", + (long)s, s->size, newsize); + } + + s->size = newsize; + s->buf = (char *)realloc(s->buf, s->size); + if (!s->buf) + die("Out of memory"); + s->reallocs++; +} + +void strbuf_append_string(strbuf_t *s, const char *str) +{ + int space, i; + + space = strbuf_empty_length(s); + + for (i = 0; str[i]; i++) { + if (space < 1) { + strbuf_resize(s, s->length + 1); + space = strbuf_empty_length(s); + } + + s->buf[s->length] = str[i]; + s->length++; + space--; + } +} + +/* strbuf_append_fmt() should only be used when an upper bound + * is known for the output string. */ +void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...) +{ + va_list arg; + int fmt_len; + + strbuf_ensure_empty_length(s, len); + + va_start(arg, fmt); + fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg); + va_end(arg); + + if (fmt_len < 0) + die("BUG: Unable to convert number"); /* This should never happen.. */ + + s->length += fmt_len; +} + +/* strbuf_append_fmt_retry() can be used when the there is no known + * upper bound for the output string. */ +void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...) +{ + va_list arg; + int fmt_len; + int empty_len; + int t; + + /* If the first attempt to append fails, resize the buffer appropriately + * and try again */ + for (t = 0; ; t++) { + va_start(arg, fmt); + /* Append the new formatted string */ + /* fmt_len is the length of the string required, excluding the + * trailing NULL */ + empty_len = strbuf_empty_length(s); + /* Add 1 since there is also space to store the terminating NULL. */ + fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg); + va_end(arg); + + if (fmt_len <= empty_len) + break; /* SUCCESS */ + if (t > 0) + die("BUG: length of formatted string changed"); + + strbuf_resize(s, s->length + fmt_len); + } + + s->length += fmt_len; +} + +/* vi:ai et sw=4 ts=4: + */ diff --git a/framework/lualib-src/lua-cjson/strbuf.h b/framework/lualib-src/lua-cjson/strbuf.h new file mode 100644 index 0000000..d861108 --- /dev/null +++ b/framework/lualib-src/lua-cjson/strbuf.h @@ -0,0 +1,154 @@ +/* strbuf - String buffer routines + * + * Copyright (c) 2010-2012 Mark Pulford + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +/* Size: Total bytes allocated to *buf + * Length: String length, excluding optional NULL terminator. + * Increment: Allocation increments when resizing the string buffer. + * Dynamic: True if created via strbuf_new() + */ + +typedef struct { + char *buf; + int size; + int length; + int increment; + int dynamic; + int reallocs; + int debug; +} strbuf_t; + +#ifndef STRBUF_DEFAULT_SIZE +#define STRBUF_DEFAULT_SIZE 1023 +#endif +#ifndef STRBUF_DEFAULT_INCREMENT +#define STRBUF_DEFAULT_INCREMENT -2 +#endif + +/* Initialise */ +extern strbuf_t *strbuf_new(int len); +extern void strbuf_init(strbuf_t *s, int len); +extern void strbuf_set_increment(strbuf_t *s, int increment); + +/* Release */ +extern void strbuf_free(strbuf_t *s); +extern char *strbuf_free_to_string(strbuf_t *s, int *len); + +/* Management */ +extern void strbuf_resize(strbuf_t *s, int len); +static int strbuf_empty_length(strbuf_t *s); +static int strbuf_length(strbuf_t *s); +static char *strbuf_string(strbuf_t *s, int *len); +static void strbuf_ensure_empty_length(strbuf_t *s, int len); +static char *strbuf_empty_ptr(strbuf_t *s); +static void strbuf_extend_length(strbuf_t *s, int len); + +/* Update */ +extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...); +extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...); +static void strbuf_append_mem(strbuf_t *s, const char *c, int len); +extern void strbuf_append_string(strbuf_t *s, const char *str); +static void strbuf_append_char(strbuf_t *s, const char c); +static void strbuf_ensure_null(strbuf_t *s); + +/* Reset string for before use */ +static inline void strbuf_reset(strbuf_t *s) +{ + s->length = 0; +} + +static inline int strbuf_allocated(strbuf_t *s) +{ + return s->buf != NULL; +} + +/* Return bytes remaining in the string buffer + * Ensure there is space for a NULL terminator. */ +static inline int strbuf_empty_length(strbuf_t *s) +{ + return s->size - s->length - 1; +} + +static inline void strbuf_ensure_empty_length(strbuf_t *s, int len) +{ + if (len > strbuf_empty_length(s)) + strbuf_resize(s, s->length + len); +} + +static inline char *strbuf_empty_ptr(strbuf_t *s) +{ + return s->buf + s->length; +} + +static inline void strbuf_extend_length(strbuf_t *s, int len) +{ + s->length += len; +} + +static inline int strbuf_length(strbuf_t *s) +{ + return s->length; +} + +static inline void strbuf_append_char(strbuf_t *s, const char c) +{ + strbuf_ensure_empty_length(s, 1); + s->buf[s->length++] = c; +} + +static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c) +{ + s->buf[s->length++] = c; +} + +static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len) +{ + strbuf_ensure_empty_length(s, len); + memcpy(s->buf + s->length, c, len); + s->length += len; +} + +static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len) +{ + memcpy(s->buf + s->length, c, len); + s->length += len; +} + +static inline void strbuf_ensure_null(strbuf_t *s) +{ + s->buf[s->length] = 0; +} + +static inline char *strbuf_string(strbuf_t *s, int *len) +{ + if (len) + *len = s->length; + + return s->buf; +} + +/* vi:ai et sw=4 ts=4: + */ diff --git a/framework/lualib-src/lua-clua/Makefile b/framework/lualib-src/lua-clua/Makefile new file mode 100644 index 0000000..e22708d --- /dev/null +++ b/framework/lualib-src/lua-clua/Makefile @@ -0,0 +1,29 @@ +SKYNET_ROOT ?= ../../skynet +include $(SKYNET_ROOT)/platform.mk + +PLAT ?= none + +TARGET = ../../luaclib/clua.so + +ifeq ($(PLAT), macosx) + CFLAGS = -g -O2 -dynamiclib -Wl,-undefined,dynamic_lookup +else +ifeq ($(PLAT), linux) + CFLAGS = -g -O2 -shared -fPIC +endif +endif + +LUA_LIB ?= $(SKYNET_ROOT)/3rd/lua/ +LUA_INC ?= $(SKYNET_ROOT)/3rd/lua/ + +SRC = . + +.PHONY: all clean + +all: $(TARGET) + +$(TARGET): $(foreach dir, $(SRC), $(wildcard $(dir)/*.cpp)) + g++ $(CFLAGS) $(SHARED) -I$(LUA_INC) $^ -o $@ + +clean: + rm -f *.o $(TARGET) \ No newline at end of file diff --git a/framework/lualib-src/lua-clua/clua.cpp b/framework/lualib-src/lua-clua/clua.cpp new file mode 100644 index 0000000..fb54bbf --- /dev/null +++ b/framework/lualib-src/lua-clua/clua.cpp @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +} + +const int open_debug = 0; + +#define LLOG(...) if (open_debug) {llog("[DEBUG] ", __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__);} +#define LERR(...) if (open_debug) {llog("[ERROR] ", __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__);} + +void llog(const char *header, const char *file, const char *func, int pos, const char *fmt, ...) { + FILE *pLog = NULL; + time_t clock1; + struct tm *tptr; + va_list ap; + + pLog = fopen("luacov.log", "a+"); + if (pLog == NULL) { + return; + } + + clock1 = time(0); + tptr = localtime(&clock1); + + struct timeval tv; + gettimeofday(&tv, NULL); + + fprintf(pLog, "===========================[%d.%d.%d, %d.%d.%d %llu]%s:%d,%s:===========================\n%s", + tptr->tm_year + 1990, tptr->tm_mon + 1, + tptr->tm_mday, tptr->tm_hour, tptr->tm_min, + tptr->tm_sec, (long long) ((tv.tv_sec) * 1000 + (tv.tv_usec) / 1000), file, pos, func, header); + + va_start(ap, fmt); + vfprintf(pLog, fmt, ap); + fprintf(pLog, "\n\n"); + va_end(ap); + + va_start(ap, fmt); + vprintf(fmt, ap); + printf("\n\n"); + va_end(ap); + + fclose(pLog); +} + +std::unordered_map gdata; +int gcount; +int glasttime; +std::string gfile; +int gautosave; + +static void flush_file(int fd, const char *buf, size_t len) { + while (len > 0) { + ssize_t r = write(fd, buf, len); + buf += r; + len -= r; + } +} + +static void flush() { + int fd = open(gfile.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666); + if (fd < 0) { + LERR("open file fail %s", gfile.c_str()); + return; + } + + for (std::unordered_map::iterator it = gdata.begin(); it != gdata.end(); it++) { + int len = it->first.length(); + flush_file(fd, (const char *) &len, sizeof(len)); + flush_file(fd, it->first.c_str(), len); + uint64_t count = it->second; + flush_file(fd, (const char *) &count, sizeof(count)); + } + + close(fd); +} + +static void hook_handler(lua_State *L, lua_Debug *par) { + if (par->event != LUA_HOOKLINE) { + LERR("hook_handler diff event %d", par->event); + return; + } + + lua_Debug ar; + ar.source = 0; + int ret = lua_getstack(L, 0, &ar); + if (ret == 0) { + LERR("hook_handler lua_getstack fail %d", ret); + return; + } + + ret = lua_getinfo(L, "S", &ar); + if (ret == 0) { + LERR("hook_handler lua_getinfo fail %d", ret); + return; + } + if (ar.source == 0) { + LERR("hook_handler source nil "); + return; + } + if (ar.source[0] != '@') { + LLOG("hook_handler source error %s ", ar.source); + return; + } + + char buff[128] = {0}; + snprintf(buff, sizeof(buff) - 1, "%d", par->currentline); + std::string d = ar.source; + d = d + ":"; + d = d + buff; + + gdata[d]++; + + if (gautosave > 0) { + gcount++; + if (gcount % 10000 == 0) { + if (time(0) - glasttime > gautosave) { + glasttime = time(0); + LLOG("hook_handler %s %d", d.c_str(), gdata.size()); + flush(); + } + } + } +} + +extern "C" void start_cov(lua_State *L, const char *file, int autosave) { + if (gdata.size() > 0) { + return; + } + gfile = file; + gautosave = autosave; + gcount = 0; + glasttime = 0; + lua_sethook(L, hook_handler, LUA_MASKLINE, 0); +} + +extern "C" void stop_cov(lua_State *L) { + lua_sethook(L, 0, 0, 0); + flush(); + gdata.clear(); +} + +static int lstart(lua_State *L) { + const char *file = lua_tostring(L, 1); + int autosave = (int) lua_tointeger(L, 2); + start_cov(L, file, autosave); + return 0; +} + +static int lstop(lua_State *L) { + stop_cov(L); + return 0; +} + +extern "C" int luaopen_clua(lua_State *L) { + luaL_checkversion(L); + luaL_Reg l[] = { + {"start", lstart}, + {"stop", lstop}, + {NULL, NULL}, + }; + luaL_newlib(L, l); + return 1; +} \ No newline at end of file diff --git a/framework/lualib-src/lua-crab/Makefile b/framework/lualib-src/lua-crab/Makefile new file mode 100644 index 0000000..04a0d74 --- /dev/null +++ b/framework/lualib-src/lua-crab/Makefile @@ -0,0 +1,30 @@ +SKYNET_ROOT ?= ../../skynet +include $(SKYNET_ROOT)/platform.mk + +PLAT ?= none + +TARGET = ../../luaclib/crab.so + +ifeq ($(PLAT), macosx) + CFLAGS = -g -O2 -dynamiclib -Wl,-undefined,dynamic_lookup +else +ifeq ($(PLAT), linux) + CFLAGS = -g -O2 -shared -fPIC +endif +endif + +LUA_LIB ?= $(SKYNET_ROOT)/3rd/lua/ +LUA_INC ?= $(SKYNET_ROOT)/3rd/lua/ +SKYNET_SRC ?= $(SKYNET_ROOT)/skynet-src + +SRC = . + +.PHONY: all clean + +all: $(TARGET) + +$(TARGET): $(foreach dir, $(SRC), $(wildcard $(dir)/*.c)) + $(CC) $(CFLAGS) $(SHARED) -I$(LUA_INC) -I$(SKYNET_SRC) $^ -o $@ + +clean: + rm -f *.o $(TARGET) \ No newline at end of file diff --git a/framework/lualib-src/lua-crab/lua-crab.c b/framework/lualib-src/lua-crab/lua-crab.c new file mode 100644 index 0000000..4fdc95e --- /dev/null +++ b/framework/lualib-src/lua-crab/lua-crab.c @@ -0,0 +1,413 @@ +#include +#include + +#include "lua.h" +#include "lauxlib.h" + +typedef struct _TableNode { + uint32_t key; + int next; + + char flag; // 0: empty, 'n': non-terminator, 'o': terminator + void* value; +} TableNode; + +typedef struct _Table { + int capacity; + + TableNode* node; + TableNode* lastfree; +} Table; + +typedef struct _Crab { + uint32_t replace_rune; + Table *trie; +} Crab; + +inline static void +initnode(TableNode *node) { + node->next = -1; + + node->flag = 0; + node->value = NULL; +} + +inline static int +tisnil(TableNode* node) { + return node->flag == 0; +} + +inline static TableNode* +tnode(Table *t, int index) { + return t->node + index; +} + +inline static int +tindex(Table *t, TableNode *node) { + return node - t->node; +} + +static TableNode* +mainposition(Table *t, uint32_t key) { + return &t->node[(key & (t->capacity -1))]; +} + +static TableNode* +getfreenode(Table *t) { + while(t->lastfree >= t->node) { + if(tisnil(t->lastfree)) { + return t->lastfree; + } + t->lastfree--; + } + return NULL; +} + +static TableNode* +table_newkey(Table *t, uint32_t key); + +static void +table_expand(Table *t) { + int capacity = t->capacity; + TableNode *node = t->node; + + // init new table + t->capacity = t->capacity * 2; + t->node = calloc(t->capacity, sizeof(TableNode)); + int i; + for(i=0; icapacity; i++) { + initnode(t->node + i); + } + t->lastfree = t->node + (t->capacity - 1); + + // reinsert old node + for(i=0; i< capacity; i++) { + TableNode *old = node + i; + if(tisnil(old)) { + continue; + } + TableNode *new = table_newkey(t, old->key); + new->flag = old->flag; + new->value = old->value; + } + // free old node + free(node); +} + +/* +** inserts a new key into a hash table; first, check whether key's main +** position is free. If not, check whether colliding node is in its main +** position or not: if it is not, move colliding node to an empty place and +** put new key in its main position; otherwise (colliding node is in its main +** position), new key goes to an empty position. +*/ +static TableNode* +table_newkey(Table *t, uint32_t key) { + TableNode *mp = mainposition(t, key); + if(!tisnil(mp)) { + TableNode *n = getfreenode(t); + if(n == NULL) { + table_expand(t); + return table_newkey(t, key); + } + TableNode *othern = mainposition(t, mp->key); + if (othern != mp) { + int mindex = tindex(t, mp); + while(othern->next != mindex) { + othern = tnode(t, othern->next); + } + othern->next = tindex(t, n); + *n = *mp; + initnode(mp); + } else { + n->next = mp->next; + mp->next = tindex(t, n); + mp = n; + } + } + mp->key = key; + mp->flag = 'n'; + return mp; +} + +static TableNode* +table_get(Table *t, uint32_t key) { + TableNode *n = mainposition(t, key); + while(!tisnil(n)) { + if(n->key == key) { + return n; + } + if(n->next < 0) { + break; + } + n = tnode(t, n->next); + } + return NULL; +} + +static TableNode* +table_insert(Table *t, uint32_t key) { + TableNode *node = table_get(t, key); + if(node) { + return node; + } + return table_newkey(t, key); +} + +static Table* +table_new() { + Table *t = malloc(sizeof(Table)); + t->capacity = 1; + + t->node = malloc(sizeof(TableNode)); + initnode(t->node); + t->lastfree = t->node; + return t; +} + +// deconstruct dictinory tree +static void +_dict_close(Table *t) { + if(t == NULL) { + return; + } + int i = 0; + for(i=0; icapacity; i++) { + TableNode *node = t->node + i; + if(node->flag != 0) { + _dict_close(node->value); + } + } + free(t->node); + free(t); +} + +static void +_dict_dump(Table *t, int indent) { + if(t == NULL) { + return; + } + int i = 0; + for(i=0; icapacity; i++) { + TableNode *node = t->node + i; + printf("%*s", indent, " "); + if(node->flag != 0) { + printf("0x%x\n", node->key); + _dict_dump(node->value, indent + 8); + } else { + printf("%s\n", "nil"); + } + } +} + +static int +_dict_insert(lua_State *L, Table* dict) { + if(!lua_istable(L, -1)) { + return 0; + } + + size_t len = lua_rawlen(L, -1); + size_t i; + uint32_t rune; + TableNode *node = NULL; + for(i=1; i<=len; i++) { + lua_rawgeti(L, -1, i); + int isnum; + rune = (uint32_t)lua_tointegerx(L, -1, &isnum); + lua_pop(L, 1); + + if(!isnum) { + return 0; + } + + Table *tmp; + if(node == NULL) { + tmp = dict; + } else { + if(node->value == NULL) { + node->value = table_new(); + } + tmp = node->value; + } + node = table_insert(tmp, rune); + } + if(node) { + node->flag = 'o'; + } + return 1; +} + +static int +dict_open(lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + Table *dict = table_new(); + size_t len = lua_rawlen(L,1); + size_t i; + for(i=1;i<=len;i++) { + lua_rawgeti(L, 1, i); + if(!_dict_insert(L, dict)) { + _dict_close(dict); + return luaL_error(L, "illegal parameters in table index %d", i); + } + lua_pop(L, 1); + } + Crab *crab = (Crab*)lua_newuserdata(L,sizeof(*crab)); + crab->trie = dict; + crab->replace_rune = '*'; + luaL_getmetatable(L,"crab_meta"); + lua_setmetatable(L,-2); + //_dict_dump(dict,0); + return 1; +} + +#define check_crab(L,idx)\ + (Crab*)luaL_checkudata(L,idx,"crab_meta") + +static int +next(lua_State *L,Table *dict,int text_idx,size_t start,size_t end,size_t *pos1,size_t *pos2) { + size_t i,j; + int flag = 0; + for(i=start;i<=end;) { + TableNode *node = NULL; + int step = 0; + for(j=i;j<=end;j++) { + lua_rawgeti(L, text_idx, j); + uint32_t rune = (uint32_t) lua_tointeger(L, -1); + lua_pop(L, 1); + + if(node == NULL) { + node = table_get(dict, rune); + } else { + node = table_get(node->value, rune); + } + + if(node && node->flag == 'o') step = j - i + 1; + if(!(node && node->value)) break; + } + if(step > 0) { + flag = 1; + *pos1 = i; + *pos2 = i + step - 1; + break; + } else { + i++; + } + } + return flag; +} + +static int +dict_next(lua_State *L) { + Crab *crab = check_crab(L,1); + Table* dict = crab->trie; + int text_idx = 2; + luaL_checktype(L, text_idx, LUA_TTABLE); + size_t len = lua_rawlen(L,text_idx); + int nargs = lua_gettop(L); + size_t start = 1; + size_t end = len; + if (nargs >= 3) { + start = luaL_checkinteger(L,3); + } + if (nargs >= 4) { + end = luaL_checkinteger(L,4); + } + if (start > end) { + return luaL_error(L, "index illegal: %d > %d", start,end); + } + size_t pos1,pos2; + int found = next(L,dict,text_idx,start,end,&pos1,&pos2); + lua_pushboolean(L,found); + if (!found) { + return 1; + } else { + lua_pushinteger(L,pos1); + lua_pushinteger(L,pos2); + return 3; + } +} + +static int +dict_filter(lua_State *L) { + Crab *crab = check_crab(L,1); + Table* dict = crab->trie; + uint32_t replace_rune = crab->replace_rune; + int text_idx = 2; + luaL_checktype(L, text_idx, LUA_TTABLE); + size_t len = lua_rawlen(L,text_idx); + int nargs = lua_gettop(L); + size_t start = 1; + size_t end = len; + if (nargs >= 3) { + start = luaL_checkinteger(L,3); + } + if (nargs >= 4) { + end = luaL_checkinteger(L,4); + } + if (start > end) { + return luaL_error(L, "index illegal: %d > %d", start,end); + } + size_t i; + size_t pos1,pos2; + int found; + int flag = 0; + found = next(L,dict,text_idx,start,end,&pos1,&pos2); + while (found) { + flag = 1; + for(i=pos1;i<=pos2;i++) { + lua_pushinteger(L, replace_rune); + lua_rawseti(L, text_idx, i); + } + start = pos2 + 1; + found = next(L,dict,text_idx,start,end,&pos1,&pos2); + } + lua_pushboolean(L, flag); + return 1; +} + +static int +dict_replace_rune(lua_State *L) { + Crab *crab = check_crab(L,1); + if (lua_gettop(L) >= 2) { + uint32_t rune = luaL_checkinteger(L,2); + lua_pushinteger(L,crab->replace_rune); + crab->replace_rune = rune; + return 1; + } else { + lua_pushinteger(L,crab->replace_rune); + return 1; + } +} + +static int crab_gc(lua_State *L) { + Crab *crab = check_crab(L,1); + _dict_close(crab->trie); + return 0; +} + +// interface +int +luaopen_crab_c(lua_State *L) { + luaL_checkversion(L); + + luaL_Reg crab_methods[] = { + {"filter", dict_filter}, + {"next", dict_next}, + {"replace_rune",dict_replace_rune}, + {NULL, NULL} + }; + luaL_newmetatable(L,"crab_meta"); + lua_newtable(L); + luaL_setfuncs(L,crab_methods,0); + lua_setfield(L,-2,"__index"); + lua_pushcfunction(L,crab_gc); + lua_setfield(L,-2,"__gc"); + + luaL_Reg l[] = { + {"open", dict_open}, + {"new",dict_open}, + }; + + luaL_newlib(L, l); + return 1; +} diff --git a/framework/lualib-src/lua-geohash/Makefile b/framework/lualib-src/lua-geohash/Makefile new file mode 100755 index 0000000..261239e --- /dev/null +++ b/framework/lualib-src/lua-geohash/Makefile @@ -0,0 +1,29 @@ +PLAT ?= none +SKYNET_ROOT ?= ../../skynet +include $(SKYNET_ROOT)/platform.mk + +TARGET = ../../luaclib/geohash.so + +ifeq ($(PLAT), macosx) + CFLAGS = -g -O2 -dynamiclib -Wl,-undefined,dynamic_lookup +else +ifeq ($(PLAT), linux) + CFLAGS = -g -O2 -shared -fPIC +endif +endif + +LUA_LIB ?= $(SKYNET_ROOT)/3rd/lua/ +LUA_INC ?= $(SKYNET_ROOT)/3rd/lua/ + + +SRC = . + +.PHONY: all clean + +all: $(TARGET) + +$(TARGET): $(foreach dir, $(SRC), $(wildcard $(dir)/*.c)) + $(CC) $(CFLAGS) $(SHARED) -I$(LUA_INC) $^ -o $@ + +clean: + rm -f *.o $(TARGET) \ No newline at end of file diff --git a/framework/lualib-src/lua-geohash/geohash.c b/framework/lualib-src/lua-geohash/geohash.c new file mode 100755 index 0000000..27d2a74 --- /dev/null +++ b/framework/lualib-src/lua-geohash/geohash.c @@ -0,0 +1,425 @@ +/* + *Copyright (c) 2013-2014, yinqiwen + *All rights reserved. + * + *Redistribution and use in source and binary forms, with or without + *modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + *THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + *AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + *IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + *ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + *BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + *CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + *SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + *INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + *CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + *ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + *THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include "geohash.h" + +/** + * Hashing works like this: + * Divide the world into 4 buckets. Label each one as such: + * ----------------- + * | | | + * | | | + * | 0,1 | 1,1 | + * ----------------- + * | | | + * | | | + * | 0,0 | 1,0 | + * ----------------- + */ + +int geohash_encode( + GeoHashRange lat_range, GeoHashRange lon_range, + double latitude, double longitude, uint8_t step, GeoHashBits *hash) +{ + if (NULL == hash || step > 32 || step == 0) + { + return -1; + } + hash->bits = 0; + hash->step = step; + uint8_t i = 0; + if (latitude < lat_range.min || latitude > lat_range.max || longitude < lon_range.min || longitude > lon_range.max) + { + return -1; + } + + for (; i < step; i++) + { + uint8_t lat_bit, lon_bit; + if (lat_range.max - latitude >= latitude - lat_range.min) + { + lat_bit = 0; + lat_range.max = (lat_range.max + lat_range.min) / 2; + } + else + { + lat_bit = 1; + lat_range.min = (lat_range.max + lat_range.min) / 2; + } + if (lon_range.max - longitude >= longitude - lon_range.min) + { + lon_bit = 0; + lon_range.max = (lon_range.max + lon_range.min) / 2; + } + else + { + lon_bit = 1; + lon_range.min = (lon_range.max + lon_range.min) / 2; + } + hash->bits <<= 1; + hash->bits += lon_bit; + hash->bits <<= 1; + hash->bits += lat_bit; + } + return 0; +} + +static inline uint8_t get_bit(uint64_t bits, uint8_t pos) +{ + return (bits >> pos) & 0x01; +} + +int geohash_decode( + GeoHashRange lat_range, GeoHashRange lon_range, GeoHashBits hash, GeoHashArea *area) +{ + if (NULL == area) + { + return -1; + } + area->hash = hash; + uint8_t i = 0; + area->latitude.min = lat_range.min; + area->latitude.max = lat_range.max; + area->longitude.min = lon_range.min; + area->longitude.max = lon_range.max; + for (; i < hash.step; i++) + { + uint8_t lat_bit, lon_bit; + lon_bit = get_bit(hash.bits, (hash.step - i) * 2 - 1); + lat_bit = get_bit(hash.bits, (hash.step - i) * 2 - 2); + if (lat_bit == 0) + { + area->latitude.max = (area->latitude.max + area->latitude.min) / 2; + } + else + { + area->latitude.min = (area->latitude.max + area->latitude.min) / 2; + } + if (lon_bit == 0) + { + area->longitude.max = (area->longitude.max + area->longitude.min) / 2; + } + else + { + area->longitude.min = (area->longitude.max + area->longitude.min) / 2; + } + } + return 0; +} + +static inline uint64_t interleave64(uint32_t xlo, uint32_t ylo) +{ + static const uint64_t B[] = + {0x5555555555555555, 0x3333333333333333, 0x0F0F0F0F0F0F0F0F, 0x00FF00FF00FF00FF, 0x0000FFFF0000FFFF}; + static const unsigned int S[] = + {1, 2, 4, 8, 16}; + + uint64_t x = xlo; // Interleave lower bits of x and y, so the bits of x + uint64_t y = ylo; // are in the even positions and bits from y in the odd; //https://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN + + // x and y must initially be less than 2**32. + + x = (x | (x << S[4])) & B[4]; + y = (y | (y << S[4])) & B[4]; + + x = (x | (x << S[3])) & B[3]; + y = (y | (y << S[3])) & B[3]; + + x = (x | (x << S[2])) & B[2]; + y = (y | (y << S[2])) & B[2]; + + x = (x | (x << S[1])) & B[1]; + y = (y | (y << S[1])) & B[1]; + + x = (x | (x << S[0])) & B[0]; + y = (y | (y << S[0])) & B[0]; + + return x | (y << 1); +} + +static inline uint64_t deinterleave64(uint64_t interleaved) +{ + static const uint64_t B[] = + {0x5555555555555555, 0x3333333333333333, 0x0F0F0F0F0F0F0F0F, 0x00FF00FF00FF00FF, 0x0000FFFF0000FFFF, + 0x00000000FFFFFFFF}; + static const unsigned int S[] = + {0, 1, 2, 4, 8, 16}; + + uint64_t x = interleaved; ///reverse the interleave process (http://stackoverflow.com/questions/4909263/how-to-efficiently-de-interleave-bits-inverse-morton) + uint64_t y = interleaved >> 1; + + x = (x | (x >> S[0])) & B[0]; + y = (y | (y >> S[0])) & B[0]; + + x = (x | (x >> S[1])) & B[1]; + y = (y | (y >> S[1])) & B[1]; + + x = (x | (x >> S[2])) & B[2]; + y = (y | (y >> S[2])) & B[2]; + + x = (x | (x >> S[3])) & B[3]; + y = (y | (y >> S[3])) & B[3]; + + x = (x | (x >> S[4])) & B[4]; + y = (y | (y >> S[4])) & B[4]; + + x = (x | (x >> S[5])) & B[5]; + y = (y | (y >> S[5])) & B[5]; + + return x | (y << 32); +} + +int geohash_fast_encode( + GeoHashRange lat_range, GeoHashRange lon_range, double latitude, + double longitude, uint8_t step, GeoHashBits *hash) +{ + if (NULL == hash || step > 32 || step == 0) + { + return -1; + } + hash->bits = 0; + hash->step = step; + if (latitude < lat_range.min || latitude > lat_range.max || longitude < lon_range.min || longitude > lon_range.max) + { + return -1; + } + + // The algorithm computes the morton code for the geohash location within + // the range this can be done MUCH more efficiently using the following code + + //compute the coordinate in the range 0-1 + double lat_offset = (latitude - lat_range.min) / (lat_range.max - lat_range.min); + double lon_offset = (longitude - lon_range.min) / (lon_range.max - lon_range.min); + + //convert it to fixed point based on the step size + lat_offset *= (1LL << step); + lon_offset *= (1LL << step); + + uint32_t ilato = (uint32_t)lat_offset; + uint32_t ilono = (uint32_t)lon_offset; + + //interleave the bits to create the morton code. No branching and no bounding + hash->bits = interleave64(ilato, ilono); + return 0; +} + +int geohash_fast_decode(GeoHashRange lat_range, GeoHashRange lon_range, GeoHashBits hash, GeoHashArea *area) +{ + if (NULL == area) + { + return -1; + } + area->hash = hash; + uint8_t step = hash.step; + uint64_t xyhilo = deinterleave64(hash.bits); //decode morton code + + double lat_scale = lat_range.max - lat_range.min; + double lon_scale = lon_range.max - lon_range.min; + + uint32_t ilato = xyhilo; //get back the original integer coordinates + uint32_t ilono = xyhilo >> 32; + + //double lat_offset=ilato; + //double lon_offset=ilono; + //lat_offset /= (1<latitude.min = lat_range.min + ldexp(ilato, -step) * lat_scale; + // area->latitude.max = lat_range.min + ldexp(ilato + 1, -step) * lat_scale; + // area->longitude.min = lon_range.min + ldexp(ilono, -step) * lon_scale; + // area->longitude.max = lon_range.min + ldexp(ilono + 1, -step) * lon_scale; + + /* + * much faster than 'ldexp' + */ + area->latitude.min = lat_range.min + (ilato * 1.0 / (1ull << step)) * lat_scale; + area->latitude.max = lat_range.min + ((ilato + 1) * 1.0 / (1ull << step)) * lat_scale; + area->longitude.min = lon_range.min + (ilono * 1.0 / (1ull << step)) * lon_scale; + area->longitude.max = lon_range.min + ((ilono + 1) * 1.0 / (1ull << step)) * lon_scale; + + return 0; +} + +static int geohash_move_x(GeoHashBits *hash, int8_t d) +{ + if (d == 0) + return 0; + uint64_t x = hash->bits & 0xaaaaaaaaaaaaaaaaLL; + uint64_t y = hash->bits & 0x5555555555555555LL; + + uint64_t zz = 0x5555555555555555LL >> (64 - hash->step * 2); + if (d > 0) + { + x = x + (zz + 1); + } + else + { + x = x | zz; + x = x - (zz + 1); + } + x &= (0xaaaaaaaaaaaaaaaaLL >> (64 - hash->step * 2)); + hash->bits = (x | y); + return 0; +} + +static int geohash_move_y(GeoHashBits *hash, int8_t d) +{ + if (d == 0) + return 0; + uint64_t x = hash->bits & 0xaaaaaaaaaaaaaaaaLL; + uint64_t y = hash->bits & 0x5555555555555555LL; + + uint64_t zz = 0xaaaaaaaaaaaaaaaaLL >> (64 - hash->step * 2); + if (d > 0) + { + y = y + (zz + 1); + } + else + { + y = y | zz; + y = y - (zz + 1); + } + y &= (0x5555555555555555LL >> (64 - hash->step * 2)); + hash->bits = (x | y); + return 0; +} + +int geohash_get_neighbors(GeoHashBits hash, GeoHashNeighbors *neighbors) +{ + geohash_get_neighbor(hash, GEOHASH_NORTH, &neighbors->north); + geohash_get_neighbor(hash, GEOHASH_EAST, &neighbors->east); + geohash_get_neighbor(hash, GEOHASH_WEST, &neighbors->west); + geohash_get_neighbor(hash, GEOHASH_SOUTH, &neighbors->south); + geohash_get_neighbor(hash, GEOHASH_SOUTH_WEST, &neighbors->south_west); + geohash_get_neighbor(hash, GEOHASH_SOUTH_EAST, &neighbors->south_east); + geohash_get_neighbor(hash, GEOHASH_NORT_WEST, &neighbors->north_west); + geohash_get_neighbor(hash, GEOHASH_NORT_EAST, &neighbors->north_east); + return 0; +} + +int geohash_get_neighbor(GeoHashBits hash, GeoDirection direction, GeoHashBits *neighbor) +{ + if (NULL == neighbor) + { + return -1; + } + *neighbor = hash; + switch (direction) + { + case GEOHASH_NORTH: + { + geohash_move_x(neighbor, 0); + geohash_move_y(neighbor, 1); + break; + } + case GEOHASH_SOUTH: + { + geohash_move_x(neighbor, 0); + geohash_move_y(neighbor, -1); + break; + } + case GEOHASH_EAST: + { + geohash_move_x(neighbor, 1); + geohash_move_y(neighbor, 0); + break; + } + case GEOHASH_WEST: + { + geohash_move_x(neighbor, -1); + geohash_move_y(neighbor, 0); + break; + } + case GEOHASH_SOUTH_WEST: + { + geohash_move_x(neighbor, -1); + geohash_move_y(neighbor, -1); + break; + } + case GEOHASH_SOUTH_EAST: + { + geohash_move_x(neighbor, 1); + geohash_move_y(neighbor, -1); + break; + } + case GEOHASH_NORT_WEST: + { + geohash_move_x(neighbor, -1); + geohash_move_y(neighbor, 1); + break; + } + case GEOHASH_NORT_EAST: + { + geohash_move_x(neighbor, 1); + geohash_move_y(neighbor, 1); + break; + } + default: + { + return -1; + } + } + return 0; +} + +GeoHashBits geohash_next_leftbottom(GeoHashBits bits) +{ + GeoHashBits newbits = bits; + newbits.step++; + newbits.bits <<= 2; + return newbits; +} +GeoHashBits geohash_next_rightbottom(GeoHashBits bits) +{ + GeoHashBits newbits = bits; + newbits.step++; + newbits.bits <<= 2; + newbits.bits += 2; + return newbits; +} +GeoHashBits geohash_next_lefttop(GeoHashBits bits) +{ + GeoHashBits newbits = bits; + newbits.step++; + newbits.bits <<= 2; + newbits.bits += 1; + return newbits; +} + +GeoHashBits geohash_next_righttop(GeoHashBits bits) +{ + GeoHashBits newbits = bits; + newbits.step++; + newbits.bits <<= 2; + newbits.bits += 3; + return newbits; +} diff --git a/framework/lualib-src/lua-geohash/geohash.h b/framework/lualib-src/lua-geohash/geohash.h new file mode 100755 index 0000000..4d7ed4d --- /dev/null +++ b/framework/lualib-src/lua-geohash/geohash.h @@ -0,0 +1,107 @@ +/* + *Copyright (c) 2013-2014, yinqiwen + *All rights reserved. + * + *Redistribution and use in source and binary forms, with or without + *modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + *THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + *AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + *IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + *ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + *BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + *CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + *SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + *INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + *CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + *ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + *THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GEOHASH_H_ +#define GEOHASH_H_ + +#include + +#if defined(__cplusplus) +extern "C" +{ +#endif + + typedef enum + { + GEOHASH_NORTH = 0, + GEOHASH_EAST, + GEOHASH_WEST, + GEOHASH_SOUTH, + GEOHASH_SOUTH_WEST, + GEOHASH_SOUTH_EAST, + GEOHASH_NORT_WEST, + GEOHASH_NORT_EAST + } GeoDirection; + + typedef struct + { + uint64_t bits; + uint8_t step; + } GeoHashBits; + + typedef struct + { + double max; + double min; + } GeoHashRange; + + typedef struct + { + GeoHashBits hash; + GeoHashRange latitude; + GeoHashRange longitude; + } GeoHashArea; + + typedef struct + { + GeoHashBits north; + GeoHashBits east; + GeoHashBits west; + GeoHashBits south; + GeoHashBits north_east; + GeoHashBits south_east; + GeoHashBits north_west; + GeoHashBits south_west; + } GeoHashNeighbors; + + /* + * 0:success + * -1:failed + */ + int geohash_encode(GeoHashRange lat_range, GeoHashRange lon_range, double latitude, double longitude, uint8_t step, GeoHashBits *hash); + int geohash_decode(GeoHashRange lat_range, GeoHashRange lon_range, GeoHashBits hash, GeoHashArea *area); + + /* + * Fast encode/decode version, more magic in implementation. + */ + int geohash_fast_encode(GeoHashRange lat_range, GeoHashRange lon_range, double latitude, double longitude, uint8_t step, GeoHashBits *hash); + int geohash_fast_decode(GeoHashRange lat_range, GeoHashRange lon_range, GeoHashBits hash, GeoHashArea *area); + + int geohash_get_neighbors(GeoHashBits hash, GeoHashNeighbors *neighbors); + int geohash_get_neighbor(GeoHashBits hash, GeoDirection direction, GeoHashBits *neighbor); + + GeoHashBits geohash_next_leftbottom(GeoHashBits bits); + GeoHashBits geohash_next_rightbottom(GeoHashBits bits); + GeoHashBits geohash_next_lefttop(GeoHashBits bits); + GeoHashBits geohash_next_righttop(GeoHashBits bits); + +#if defined(__cplusplus) +} +#endif +#endif /* GEOHASH_H_ */ diff --git a/framework/lualib-src/lua-geohash/lua-geohash.c b/framework/lualib-src/lua-geohash/lua-geohash.c new file mode 100644 index 0000000..47be339 --- /dev/null +++ b/framework/lualib-src/lua-geohash/lua-geohash.c @@ -0,0 +1,74 @@ +#include +#include + +#include "lua.h" +#include "lauxlib.h" +#include "geohash.h" + +// 算法 来源 https://github.com/yinqiwen/geohash-int + +static int _geohash_encode(lua_State *L) +{ + double lat = (double)luaL_checknumber(L, 1); + double lon = (double)luaL_checknumber(L, 2); + uint8_t step = (uint8_t)luaL_checknumber(L, 3); + + // 纬度 + double lat_min = (double)luaL_checknumber(L, 4); + double lat_max = (double)luaL_checknumber(L, 5); + + // 经度 + double lon_min = (double)luaL_checknumber(L, 6); + double lon_max = (double)luaL_checknumber(L, 7); + + // printf("==== _geohash_encode latL:%lf lon:%lf step:%d ===== \n", lat, lon, step); + // printf(" ^^^^^^ _geohash_encode lat_max:%lf lat_min:%lf lon_max:%lf lon_min:%lf ^^^^ \n", lat_max, lat_min, lon_max, lon_min); + + GeoHashRange lat_range, lon_range; + lat_range.max = lat_max; + lat_range.min = lat_min; + lon_range.max = lon_max; + lon_range.min = lon_min; + + GeoHashBits fast_hash; + geohash_fast_encode(lat_range, lon_range, lat, lon, step, &fast_hash); + lua_pushnumber(L, fast_hash.bits); + return 1; +} + +static int _geohash_neighbors(lua_State *L) +{ + double bits = (double)luaL_checknumber(L, 1); + uint8_t step = (uint8_t)luaL_checknumber(L, 2); + + GeoHashBits fast_hash; + fast_hash.bits = bits; + fast_hash.step = step; + + GeoHashNeighbors neighbors; + geohash_get_neighbors(fast_hash, &neighbors); + + lua_pushnumber(L, neighbors.north.bits); + lua_pushnumber(L, neighbors.north_east.bits); + lua_pushnumber(L, neighbors.north_west.bits); + lua_pushnumber(L, neighbors.south.bits); + lua_pushnumber(L, neighbors.south_east.bits); + lua_pushnumber(L, neighbors.south_west.bits); + lua_pushnumber(L, neighbors.east.bits); + lua_pushnumber(L, neighbors.west.bits); + + return 8; +} + +LUALIB_API int luaopen_geohash(lua_State *L) +{ + luaL_checkversion(L); + + luaL_Reg l[] = { + {"hashfastencode", _geohash_encode}, + {"hashgetneighbors", _geohash_neighbors}, + {NULL, NULL}}; + + luaL_newlib(L, l); + return 1; +} diff --git a/framework/lualib-src/lua-lfs/Makefile b/framework/lualib-src/lua-lfs/Makefile new file mode 100644 index 0000000..59d93a3 --- /dev/null +++ b/framework/lualib-src/lua-lfs/Makefile @@ -0,0 +1,30 @@ +SKYNET_ROOT ?= ../../skynet +include $(SKYNET_ROOT)/platform.mk + +PLAT ?= none + +TARGET = ../../luaclib/lfs.so + +ifeq ($(PLAT), macosx) + CFLAGS = -g -O2 -dynamiclib -Wl,-undefined,dynamic_lookup +else +ifeq ($(PLAT), linux) + CFLAGS = -g -O2 -shared -fPIC +endif +endif + +LUA_LIB ?= $(SKYNET_ROOT)/3rd/lua/ +LUA_INC ?= $(SKYNET_ROOT)/3rd/lua/ +SKYNET_SRC ?= $(SKYNET_ROOT)/skynet-src + +SRC = . + +.PHONY: all clean + +all: $(TARGET) + +$(TARGET): $(foreach dir, $(SRC), $(wildcard $(dir)/*.c)) + $(CC) $(CFLAGS) $(SHARED) -I$(LUA_INC) -I$(SKYNET_SRC) $^ -o $@ + +clean: + rm -f *.o $(TARGET) \ No newline at end of file diff --git a/framework/lualib-src/lua-lfs/lfs.c b/framework/lualib-src/lua-lfs/lfs.c new file mode 100644 index 0000000..9f59f47 --- /dev/null +++ b/framework/lualib-src/lua-lfs/lfs.c @@ -0,0 +1,955 @@ +/* +** LuaFileSystem +** Copyright Kepler Project 2003 - 2017 (http://keplerproject.github.io/luafilesystem) +** +** File system manipulation library. +** This library offers these functions: +** lfs.attributes (filepath [, attributename | attributetable]) +** lfs.chdir (path) +** lfs.currentdir () +** lfs.dir (path) +** lfs.link (old, new[, symlink]) +** lfs.lock (fh, mode) +** lfs.lock_dir (path) +** lfs.mkdir (path) +** lfs.rmdir (path) +** lfs.setmode (filepath, mode) +** lfs.symlinkattributes (filepath [, attributename]) +** lfs.touch (filepath [, atime [, mtime]]) +** lfs.unlock (fh) +*/ + +#ifndef LFS_DO_NOT_USE_LARGE_FILE +#ifndef _WIN32 +#ifndef _AIX +#define _FILE_OFFSET_BITS 64 /* Linux, Solaris and HP-UX */ +#else +#define _LARGE_FILES 1 /* AIX */ +#endif +#endif +#endif + +#ifndef LFS_DO_NOT_USE_LARGE_FILE +#define _LARGEFILE64_SOURCE +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + #include + #include + #include + #include + #ifdef __BORLANDC__ + #include + #else + #include + #endif + #include + /* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */ + #define LFS_MAXPATHLEN MAX_PATH +#else + #include + #include + #include + #include + #include + #include /* for MAXPATHLEN */ + #define LFS_MAXPATHLEN MAXPATHLEN +#endif + +#include +#include +#include + +#include "lfs.h" + +#define LFS_VERSION "1.7.0" +#define LFS_LIBNAME "lfs" + +#if LUA_VERSION_NUM >= 503 /* Lua 5.3 */ + +#ifndef luaL_optlong +#define luaL_optlong luaL_optinteger +#endif + +#endif + +#if LUA_VERSION_NUM >= 502 +# define new_lib(L, l) (luaL_newlib(L, l)) +#else +# define new_lib(L, l) (lua_newtable(L), luaL_register(L, NULL, l)) +#endif + +/* Define 'strerror' for systems that do not implement it */ +#ifdef NO_STRERROR +#define strerror(_) "System unable to describe the error" +#endif + +#define DIR_METATABLE "directory metatable" +typedef struct dir_data { + int closed; +#ifdef _WIN32 + intptr_t hFile; + char pattern[MAX_PATH+1]; +#else + DIR *dir; +#endif +} dir_data; + +#define LOCK_METATABLE "lock metatable" + +#ifdef _WIN32 + #ifdef __BORLANDC__ + #define lfs_setmode(file, m) (setmode(_fileno(file), m)) + #define STAT_STRUCT struct stati64 + #else + #define lfs_setmode(file, m) (_setmode(_fileno(file), m)) + #define STAT_STRUCT struct _stati64 + #endif +#define STAT_FUNC _stati64 +#define LSTAT_FUNC STAT_FUNC +#else +#define _O_TEXT 0 +#define _O_BINARY 0 +#define lfs_setmode(file, m) ((void)file, (void)m, 0) +#define STAT_STRUCT struct stat +#define STAT_FUNC stat +#define LSTAT_FUNC lstat +#endif + +#ifdef _WIN32 + #define lfs_mkdir _mkdir +#else + #define lfs_mkdir(path) (mkdir((path), \ + S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH)) +#endif + +/* +** Utility functions +*/ +static int pusherror(lua_State *L, const char *info) +{ + lua_pushnil(L); + if (info==NULL) + lua_pushstring(L, strerror(errno)); + else + lua_pushfstring(L, "%s: %s", info, strerror(errno)); + lua_pushinteger(L, errno); + return 3; +} + +static int pushresult(lua_State *L, int res, const char *info) { + if (res == -1) { + return pusherror(L, info); + } else { + lua_pushboolean(L, 1); + return 1; + } +} + + +/* +** This function changes the working (current) directory +*/ +static int change_dir (lua_State *L) { + const char *path = luaL_checkstring(L, 1); + if (chdir(path)) { + lua_pushnil (L); + lua_pushfstring (L,"Unable to change working directory to '%s'\n%s\n", + path, chdir_error); + return 2; + } else { + lua_pushboolean (L, 1); + return 1; + } +} + +/* +** This function returns the current directory +** If unable to get the current directory, it returns nil +** and a string describing the error +*/ +static int get_dir (lua_State *L) { +#ifdef NO_GETCWD + lua_pushnil(L); + lua_pushstring(L, "Function 'getcwd' not provided by system"); + return 2; +#else + char *path = NULL; + /* Passing (NULL, 0) is not guaranteed to work. Use a temp buffer and size instead. */ + size_t size = LFS_MAXPATHLEN; /* initial buffer size */ + int result; + while (1) { + char* path2 = realloc(path, size); + if (!path2) /* failed to allocate */ { + result = pusherror(L, "get_dir realloc() failed"); + break; + } + path = path2; + if (getcwd(path, size) != NULL) { + /* success, push the path to the Lua stack */ + lua_pushstring(L, path); + result = 1; + break; + } + if (errno != ERANGE) { /* unexpected error */ + result = pusherror(L, "get_dir getcwd() failed"); + break; + } + /* ERANGE = insufficient buffer capacity, double size and retry */ + size *= 2; + } + free(path); + return result; +#endif +} + +// 判断文件是否存在 +static int lfs_exist (lua_State *L) { + const char *path = luaL_checkstring(L, 1); + lua_pushboolean (L, !access(path,0)); + return 1; +} + +/* +** Check if the given element on the stack is a file and returns it. +*/ +static FILE *check_file (lua_State *L, int idx, const char *funcname) { +#if LUA_VERSION_NUM == 501 + FILE **fh = (FILE **)luaL_checkudata (L, idx, "FILE*"); + if (*fh == NULL) { + luaL_error (L, "%s: closed file", funcname); + return 0; + } else + return *fh; +#elif LUA_VERSION_NUM >= 502 && LUA_VERSION_NUM <= 504 + luaL_Stream *fh = (luaL_Stream *)luaL_checkudata (L, idx, "FILE*"); + if (fh->closef == 0 || fh->f == NULL) { + luaL_error (L, "%s: closed file", funcname); + return 0; + } else + return fh->f; +#else +#error unsupported Lua version +#endif +} + + +/* +** +*/ +static int _file_lock (lua_State *L, FILE *fh, const char *mode, const long start, long len, const char *funcname) { + int code; +#ifdef _WIN32 + /* lkmode valid values are: + LK_LOCK Locks the specified bytes. If the bytes cannot be locked, the program immediately tries again after 1 second. If, after 10 attempts, the bytes cannot be locked, the constant returns an error. + LK_NBLCK Locks the specified bytes. If the bytes cannot be locked, the constant returns an error. + LK_NBRLCK Same as _LK_NBLCK. + LK_RLCK Same as _LK_LOCK. + LK_UNLCK Unlocks the specified bytes, which must have been previously locked. + + Regions should be locked only briefly and should be unlocked before closing a file or exiting the program. + + http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__locking.asp + */ + int lkmode; + switch (*mode) { + case 'r': lkmode = LK_NBLCK; break; + case 'w': lkmode = LK_NBLCK; break; + case 'u': lkmode = LK_UNLCK; break; + default : return luaL_error (L, "%s: invalid mode", funcname); + } + if (!len) { + fseek (fh, 0L, SEEK_END); + len = ftell (fh); + } + fseek (fh, start, SEEK_SET); +#ifdef __BORLANDC__ + code = locking (fileno(fh), lkmode, len); +#else + code = _locking (fileno(fh), lkmode, len); +#endif +#else + struct flock f; + switch (*mode) { + case 'w': f.l_type = F_WRLCK; break; + case 'r': f.l_type = F_RDLCK; break; + case 'u': f.l_type = F_UNLCK; break; + default : return luaL_error (L, "%s: invalid mode", funcname); + } + f.l_whence = SEEK_SET; + f.l_start = (off_t)start; + f.l_len = (off_t)len; + code = fcntl (fileno(fh), F_SETLK, &f); +#endif + return (code != -1); +} + +#ifdef _WIN32 +typedef struct lfs_Lock { + HANDLE fd; +} lfs_Lock; +static int lfs_lock_dir(lua_State *L) { + size_t pathl; HANDLE fd; + lfs_Lock *lock; + char *ln; + const char *lockfile = "/lockfile.lfs"; + const char *path = luaL_checklstring(L, 1, &pathl); + ln = (char*)malloc(pathl + strlen(lockfile) + 1); + if(!ln) { + lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; + } + strcpy(ln, path); strcat(ln, lockfile); + if((fd = CreateFile(ln, GENERIC_WRITE, 0, NULL, CREATE_NEW, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL)) == INVALID_HANDLE_VALUE) { + int en = GetLastError(); + free(ln); lua_pushnil(L); + if(en == ERROR_FILE_EXISTS || en == ERROR_SHARING_VIOLATION) + lua_pushstring(L, "File exists"); + else + lua_pushstring(L, strerror(en)); + return 2; + } + free(ln); + lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock)); + lock->fd = fd; + luaL_getmetatable (L, LOCK_METATABLE); + lua_setmetatable (L, -2); + return 1; +} +static int lfs_unlock_dir(lua_State *L) { + lfs_Lock *lock = (lfs_Lock *)luaL_checkudata(L, 1, LOCK_METATABLE); + if(lock->fd != INVALID_HANDLE_VALUE) { + CloseHandle(lock->fd); + lock->fd=INVALID_HANDLE_VALUE; + } + return 0; +} +#else +typedef struct lfs_Lock { + char *ln; +} lfs_Lock; +static int lfs_lock_dir(lua_State *L) { + lfs_Lock *lock; + size_t pathl; + char *ln; + const char *lockfile = "/lockfile.lfs"; + const char *path = luaL_checklstring(L, 1, &pathl); + lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock)); + ln = (char*)malloc(pathl + strlen(lockfile) + 1); + if(!ln) { + lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; + } + strcpy(ln, path); strcat(ln, lockfile); + if(symlink("lock", ln) == -1) { + free(ln); lua_pushnil(L); + lua_pushstring(L, strerror(errno)); return 2; + } + lock->ln = ln; + luaL_getmetatable (L, LOCK_METATABLE); + lua_setmetatable (L, -2); + return 1; +} +static int lfs_unlock_dir(lua_State *L) { + lfs_Lock *lock = (lfs_Lock *)luaL_checkudata(L, 1, LOCK_METATABLE); + if(lock->ln) { + unlink(lock->ln); + free(lock->ln); + lock->ln = NULL; + } + return 0; +} +#endif + +static int lfs_g_setmode (lua_State *L, FILE *f, int arg) { + static const int mode[] = {_O_BINARY, _O_TEXT}; + static const char *const modenames[] = {"binary", "text", NULL}; + int op = luaL_checkoption(L, arg, NULL, modenames); + int res = lfs_setmode(f, mode[op]); + if (res != -1) { + int i; + lua_pushboolean(L, 1); + for (i = 0; modenames[i] != NULL; i++) { + if (mode[i] == res) { + lua_pushstring(L, modenames[i]); + return 2; + } + } + lua_pushnil(L); + return 2; + } else { + return pusherror(L, NULL); + } +} + +static int lfs_f_setmode(lua_State *L) { + return lfs_g_setmode(L, check_file(L, 1, "setmode"), 2); +} + +/* +** Locks a file. +** @param #1 File handle. +** @param #2 String with lock mode ('w'rite, 'r'ead). +** @param #3 Number with start position (optional). +** @param #4 Number with length (optional). +*/ +static int file_lock (lua_State *L) { + FILE *fh = check_file (L, 1, "lock"); + const char *mode = luaL_checkstring (L, 2); + const long start = (long) luaL_optinteger (L, 3, 0); + long len = (long) luaL_optinteger (L, 4, 0); + if (_file_lock (L, fh, mode, start, len, "lock")) { + lua_pushboolean (L, 1); + return 1; + } else { + lua_pushnil (L); + lua_pushfstring (L, "%s", strerror(errno)); + return 2; + } +} + + +/* +** Unlocks a file. +** @param #1 File handle. +** @param #2 Number with start position (optional). +** @param #3 Number with length (optional). +*/ +static int file_unlock (lua_State *L) { + FILE *fh = check_file (L, 1, "unlock"); + const long start = (long) luaL_optinteger (L, 2, 0); + long len = (long) luaL_optinteger (L, 3, 0); + if (_file_lock (L, fh, "u", start, len, "unlock")) { + lua_pushboolean (L, 1); + return 1; + } else { + lua_pushnil (L); + lua_pushfstring (L, "%s", strerror(errno)); + return 2; + } +} + + +/* +** Creates a link. +** @param #1 Object to link to. +** @param #2 Name of link. +** @param #3 True if link is symbolic (optional). +*/ +static int make_link (lua_State *L) { +#ifndef _WIN32 + const char *oldpath = luaL_checkstring(L, 1); + const char *newpath = luaL_checkstring(L, 2); + int res = (lua_toboolean(L,3) ? symlink : link)(oldpath, newpath); + if (res == -1) { + return pusherror(L, NULL); + } else { + lua_pushinteger(L, 0); + return 1; + } +#else + errno = ENOSYS; /* = "Function not implemented" */ + return pushresult(L, -1, "make_link is not supported on Windows"); +#endif +} + + +/* +** Creates a directory. +** @param #1 Directory path. +*/ +static int make_dir (lua_State *L) { + const char *path = luaL_checkstring(L, 1); + return pushresult(L, lfs_mkdir(path), NULL); +} + + +/* +** Removes a directory. +** @param #1 Directory path. +*/ +static int remove_dir (lua_State *L) { + const char *path = luaL_checkstring(L, 1); + return pushresult(L, rmdir(path), NULL); +} + + +/* +** Directory iterator +*/ +static int dir_iter (lua_State *L) { +#ifdef _WIN32 + struct _finddata_t c_file; +#else + struct dirent *entry; +#endif + dir_data *d = (dir_data *)luaL_checkudata (L, 1, DIR_METATABLE); + luaL_argcheck (L, d->closed == 0, 1, "closed directory"); +#ifdef _WIN32 + if (d->hFile == 0L) { /* first entry */ + if ((d->hFile = _findfirst (d->pattern, &c_file)) == -1L) { + lua_pushnil (L); + lua_pushstring (L, strerror (errno)); + d->closed = 1; + return 2; + } else { + lua_pushstring (L, c_file.name); + return 1; + } + } else { /* next entry */ + if (_findnext (d->hFile, &c_file) == -1L) { + /* no more entries => close directory */ + _findclose (d->hFile); + d->closed = 1; + return 0; + } else { + lua_pushstring (L, c_file.name); + return 1; + } + } +#else + if ((entry = readdir (d->dir)) != NULL) { + lua_pushstring (L, entry->d_name); + return 1; + } else { + /* no more entries => close directory */ + closedir (d->dir); + d->closed = 1; + return 0; + } +#endif +} + + +/* +** Closes directory iterators +*/ +static int dir_close (lua_State *L) { + dir_data *d = (dir_data *)lua_touserdata (L, 1); +#ifdef _WIN32 + if (!d->closed && d->hFile) { + _findclose (d->hFile); + } +#else + if (!d->closed && d->dir) { + closedir (d->dir); + } +#endif + d->closed = 1; + return 0; +} + + +/* +** Factory of directory iterators +*/ +static int dir_iter_factory (lua_State *L) { + const char *path = luaL_checkstring (L, 1); + dir_data *d; + lua_pushcfunction (L, dir_iter); + d = (dir_data *) lua_newuserdata (L, sizeof(dir_data)); + luaL_getmetatable (L, DIR_METATABLE); + lua_setmetatable (L, -2); + d->closed = 0; +#ifdef _WIN32 + d->hFile = 0L; + if (strlen(path) > MAX_PATH-2) + luaL_error (L, "path too long: %s", path); + else + sprintf (d->pattern, "%s/*", path); +#else + d->dir = opendir (path); + if (d->dir == NULL) + luaL_error (L, "cannot open %s: %s", path, strerror (errno)); +#endif + return 2; +} + + +/* +** Creates directory metatable. +*/ +static int dir_create_meta (lua_State *L) { + luaL_newmetatable (L, DIR_METATABLE); + + /* Method table */ + lua_newtable(L); + lua_pushcfunction (L, dir_iter); + lua_setfield(L, -2, "next"); + lua_pushcfunction (L, dir_close); + lua_setfield(L, -2, "close"); + + /* Metamethods */ + lua_setfield(L, -2, "__index"); + lua_pushcfunction (L, dir_close); + lua_setfield (L, -2, "__gc"); + return 1; +} + + +/* +** Creates lock metatable. +*/ +static int lock_create_meta (lua_State *L) { + luaL_newmetatable (L, LOCK_METATABLE); + + /* Method table */ + lua_newtable(L); + lua_pushcfunction(L, lfs_unlock_dir); + lua_setfield(L, -2, "free"); + + /* Metamethods */ + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, lfs_unlock_dir); + lua_setfield(L, -2, "__gc"); + return 1; +} + + +#ifdef _WIN32 + #ifndef S_ISDIR + #define S_ISDIR(mode) (mode&_S_IFDIR) + #endif + #ifndef S_ISREG + #define S_ISREG(mode) (mode&_S_IFREG) + #endif + #ifndef S_ISLNK + #define S_ISLNK(mode) (0) + #endif + #ifndef S_ISSOCK + #define S_ISSOCK(mode) (0) + #endif + #ifndef S_ISFIFO + #define S_ISFIFO(mode) (0) + #endif + #ifndef S_ISCHR + #define S_ISCHR(mode) (mode&_S_IFCHR) + #endif + #ifndef S_ISBLK + #define S_ISBLK(mode) (0) + #endif +#endif +/* +** Convert the inode protection mode to a string. +*/ +#ifdef _WIN32 +static const char *mode2string (unsigned short mode) { +#else +static const char *mode2string (mode_t mode) { +#endif + if ( S_ISREG(mode) ) + return "file"; + else if ( S_ISDIR(mode) ) + return "directory"; + else if ( S_ISLNK(mode) ) + return "link"; + else if ( S_ISSOCK(mode) ) + return "socket"; + else if ( S_ISFIFO(mode) ) + return "named pipe"; + else if ( S_ISCHR(mode) ) + return "char device"; + else if ( S_ISBLK(mode) ) + return "block device"; + else + return "other"; +} + + +/* +** Set access time and modification values for a file. +** @param #1 File path. +** @param #2 Access time in seconds, current time is used if missing. +** @param #3 Modification time in seconds, access time is used if missing. +*/ +static int file_utime (lua_State *L) { + const char *file = luaL_checkstring(L, 1); + struct utimbuf utb, *buf; + + if (lua_gettop (L) == 1) /* set to current date/time */ + buf = NULL; + else { + utb.actime = (time_t) luaL_optnumber(L, 2, 0); + utb.modtime = (time_t) luaL_optinteger(L, 3, utb.actime); + buf = &utb; + } + + return pushresult(L, utime(file, buf), NULL); +} + + +/* inode protection mode */ +static void push_st_mode (lua_State *L, STAT_STRUCT *info) { + lua_pushstring (L, mode2string (info->st_mode)); +} +/* device inode resides on */ +static void push_st_dev (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_dev); +} +/* inode's number */ +static void push_st_ino (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_ino); +} +/* number of hard links to the file */ +static void push_st_nlink (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_nlink); +} +/* user-id of owner */ +static void push_st_uid (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_uid); +} +/* group-id of owner */ +static void push_st_gid (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_gid); +} +/* device type, for special file inode */ +static void push_st_rdev (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_rdev); +} +/* time of last access */ +static void push_st_atime (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_atime); +} +/* time of last data modification */ +static void push_st_mtime (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_mtime); +} +/* time of last file status change */ +static void push_st_ctime (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_ctime); +} +/* file size, in bytes */ +static void push_st_size (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_size); +} +#ifndef _WIN32 +/* blocks allocated for file */ +static void push_st_blocks (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_blocks); +} +/* optimal file system I/O blocksize */ +static void push_st_blksize (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_blksize); +} +#endif + + /* +** Convert the inode protection mode to a permission list. +*/ + +#ifdef _WIN32 +static const char *perm2string (unsigned short mode) { + static char perms[10] = "---------"; + int i; + for (i=0;i<9;i++) perms[i]='-'; + if (mode & _S_IREAD) + { perms[0] = 'r'; perms[3] = 'r'; perms[6] = 'r'; } + if (mode & _S_IWRITE) + { perms[1] = 'w'; perms[4] = 'w'; perms[7] = 'w'; } + if (mode & _S_IEXEC) + { perms[2] = 'x'; perms[5] = 'x'; perms[8] = 'x'; } + return perms; +} +#else +static const char *perm2string (mode_t mode) { + static char perms[10] = "---------"; + int i; + for (i=0;i<9;i++) perms[i]='-'; + if (mode & S_IRUSR) perms[0] = 'r'; + if (mode & S_IWUSR) perms[1] = 'w'; + if (mode & S_IXUSR) perms[2] = 'x'; + if (mode & S_IRGRP) perms[3] = 'r'; + if (mode & S_IWGRP) perms[4] = 'w'; + if (mode & S_IXGRP) perms[5] = 'x'; + if (mode & S_IROTH) perms[6] = 'r'; + if (mode & S_IWOTH) perms[7] = 'w'; + if (mode & S_IXOTH) perms[8] = 'x'; + return perms; +} +#endif + +/* permssions string */ +static void push_st_perm (lua_State *L, STAT_STRUCT *info) { + lua_pushstring (L, perm2string (info->st_mode)); +} + +typedef void (*_push_function) (lua_State *L, STAT_STRUCT *info); + +struct _stat_members { + const char *name; + _push_function push; +}; + +struct _stat_members members[] = { + { "mode", push_st_mode }, + { "dev", push_st_dev }, + { "ino", push_st_ino }, + { "nlink", push_st_nlink }, + { "uid", push_st_uid }, + { "gid", push_st_gid }, + { "rdev", push_st_rdev }, + { "access", push_st_atime }, + { "modification", push_st_mtime }, + { "change", push_st_ctime }, + { "size", push_st_size }, + { "permissions", push_st_perm }, +#ifndef _WIN32 + { "blocks", push_st_blocks }, + { "blksize", push_st_blksize }, +#endif + { NULL, NULL } +}; + +/* +** Get file or symbolic link information +*/ +static int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT*)) { + STAT_STRUCT info; + const char *file = luaL_checkstring (L, 1); + int i; + + if (st(file, &info)) { + lua_pushnil(L); + lua_pushfstring(L, "cannot obtain information from file '%s': %s", file, strerror(errno)); + lua_pushinteger(L, errno); + return 3; + } + if (lua_isstring (L, 2)) { + const char *member = lua_tostring (L, 2); + for (i = 0; members[i].name; i++) { + if (strcmp(members[i].name, member) == 0) { + /* push member value and return */ + members[i].push (L, &info); + return 1; + } + } + /* member not found */ + return luaL_error(L, "invalid attribute name '%s'", member); + } + /* creates a table if none is given, removes extra arguments */ + lua_settop(L, 2); + if (!lua_istable (L, 2)) { + lua_newtable (L); + } + /* stores all members in table on top of the stack */ + for (i = 0; members[i].name; i++) { + lua_pushstring (L, members[i].name); + members[i].push (L, &info); + lua_rawset (L, -3); + } + return 1; +} + + +/* +** Get file information using stat. +*/ +static int file_info (lua_State *L) { + return _file_info_ (L, STAT_FUNC); +} + + +/* +** Push the symlink target to the top of the stack. +** Assumes the file name is at position 1 of the stack. +** Returns 1 if successful (with the target on top of the stack), +** 0 on failure (with stack unchanged, and errno set). +*/ +static int push_link_target(lua_State *L) { +#ifdef _WIN32 + errno = ENOSYS; + return 0; +#else + const char *file = luaL_checkstring(L, 1); + char *target = NULL; + int tsize, size = 256; /* size = initial buffer capacity */ + while (1) { + char* target2 = realloc(target, size); + if (!target2) { /* failed to allocate */ + free(target); + return 0; + } + target = target2; + tsize = readlink(file, target, size); + if (tsize < 0) { /* a readlink() error occurred */ + free(target); + return 0; + } + if (tsize < size) + break; + /* possibly truncated readlink() result, double size and retry */ + size *= 2; + } + target[tsize] = '\0'; + lua_pushlstring(L, target, tsize); + free(target); + return 1; +#endif +} + +/* +** Get symbolic link information using lstat. +*/ +static int link_info (lua_State *L) { + int ret; + if (lua_isstring (L, 2) && (strcmp(lua_tostring(L, 2), "target") == 0)) { + int ok = push_link_target(L); + return ok ? 1 : pusherror(L, "could not obtain link target"); + } + ret = _file_info_ (L, LSTAT_FUNC); + if (ret == 1 && lua_type(L, -1) == LUA_TTABLE) { + int ok = push_link_target(L); + if (ok) { + lua_setfield(L, -2, "target"); + } + } + return ret; +} + + +/* +** Assumes the table is on top of the stack. +*/ +static void set_info (lua_State *L) { + lua_pushliteral(L, "Copyright (C) 2003-2017 Kepler Project"); + lua_setfield(L, -2, "_COPYRIGHT"); + lua_pushliteral(L, "LuaFileSystem is a Lua library developed to complement the set of functions related to file systems offered by the standard Lua distribution"); + lua_setfield(L, -2, "_DESCRIPTION"); + lua_pushliteral(L, "LuaFileSystem " LFS_VERSION); + lua_setfield(L, -2, "_VERSION"); +} + + +static const struct luaL_Reg fslib[] = { + {"attributes", file_info}, + {"chdir", change_dir}, + {"currentdir", get_dir}, + {"dir", dir_iter_factory}, + {"link", make_link}, + {"lock", file_lock}, + {"mkdir", make_dir}, + {"rmdir", remove_dir}, + {"symlinkattributes", link_info}, + {"setmode", lfs_f_setmode}, + {"touch", file_utime}, + {"unlock", file_unlock}, + {"lock_dir", lfs_lock_dir}, + {"exist", lfs_exist}, + {NULL, NULL}, +}; + +LFS_EXPORT int luaopen_lfs (lua_State *L) { + dir_create_meta (L); + lock_create_meta (L); + new_lib (L, fslib); + lua_pushvalue(L, -1); + lua_setglobal(L, LFS_LIBNAME); + set_info (L); + return 1; +} diff --git a/framework/lualib-src/lua-lfs/lfs.h b/framework/lualib-src/lua-lfs/lfs.h new file mode 100644 index 0000000..da0bf30 --- /dev/null +++ b/framework/lualib-src/lua-lfs/lfs.h @@ -0,0 +1,34 @@ +/* +** LuaFileSystem +** Copyright Kepler Project 2003 - 2017 (http://keplerproject.github.io/luafilesystem) +*/ + +/* Define 'chdir' for systems that do not implement it */ +#ifdef NO_CHDIR + #define chdir(p) (-1) + #define chdir_error "Function 'chdir' not provided by system" +#else + #define chdir_error strerror(errno) +#endif + +#ifdef _WIN32 + #define chdir(p) (_chdir(p)) + #define getcwd(d, s) (_getcwd(d, s)) + #define rmdir(p) (_rmdir(p)) + #define LFS_EXPORT __declspec (dllexport) + #ifndef fileno + #define fileno(f) (_fileno(f)) + #endif +#else + #define LFS_EXPORT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +LFS_EXPORT int luaopen_lfs (lua_State *L); + +#ifdef __cplusplus +} +#endif diff --git a/framework/lualib-src/lua-profile/Makefile b/framework/lualib-src/lua-profile/Makefile new file mode 100644 index 0000000..5a2bd80 --- /dev/null +++ b/framework/lualib-src/lua-profile/Makefile @@ -0,0 +1,45 @@ +SKYNET_ROOT ?= ../../skynet +include $(SKYNET_ROOT)/platform.mk + +PLAT ?= none + +TARGET = ../../luaclib/profile.so + +GOOGLE_PROFILE_INC ?= /usr/include +GOOGLE_PROFILE_LIB ?= /usr/lib64 + +ifeq ($(PLAT), macosx) + CFLAGS = -g -O2 -dynamiclib -Wl,-undefined,dynamic_lookup -DUSE_EXPORT_NAME -DUSE_RDTSC -DUSE_GOOGLE_PROFILER + + GOOGLE_PROFILE_INC = /usr/local/opt/gperftools/include + GOOGLE_PROFILE_LIB = /usr/local/opt/gperftools/lib +else +ifeq ($(PLAT), linux) + CFLAGS = -g -O2 -shared -fPIC -DUSE_EXPORT_NAME -DUSE_RDTSC -DUSE_GOOGLE_PROFILER +endif +endif + +LUA_LIB ?= $(SKYNET_ROOT)/3rd/lua/ +LUA_INC ?= $(SKYNET_ROOT)/3rd/lua/ +SKYNET_SRC ?= $(SKYNET_ROOT)/skynet-src + +SRC = . + +.PHONY: all clean + +# 生产环境 最好别用 gperftools +# mac: brew install gperftools +# centos: yum install gperftools yum install gperftools-devel +# https://github.com/google/pprof/blob/master/doc/README.md +# https://gperftools.github.io/gperftools/cpuprofile.html +# https://cloud.tencent.com/developer/article/1433465 + +# pprof skynet/skynet log/swt.prof --svg (或者 --text) +# pprof --callgrind skynet/skynet log/swt.prof > swt.callgrind +all: $(TARGET) + +$(TARGET): $(foreach dir, $(SRC), $(wildcard $(dir)/*.c)) + $(CC) $(CFLAGS) $(SHARED) -I$(LUA_INC) -I$(SKYNET_SRC) -I$(GOOGLE_PROFILE_INC) -L$(GOOGLE_PROFILE_LIB) $^ -o $@ -lprofiler + +clean: + rm -f *.o $(TARGET) \ No newline at end of file diff --git a/framework/lualib-src/lua-profile/README.md b/framework/lualib-src/lua-profile/README.md new file mode 100644 index 0000000..375c625 --- /dev/null +++ b/framework/lualib-src/lua-profile/README.md @@ -0,0 +1,24 @@ +# lua profile lib + +profile c and lua fucntion and support coroutine yield. + +~~~.lua +local profile = require "profile" + +profile.start() + +-- your code + +profile.dstop(32) -- dump top 32 call info +-- output example +--[[ + +------- dump profile ------- +[1] userdata: 0x7fc7e0c09990 name:loop file:[L]@test.lua:48 count:3 total:0.272042s ave:0.090681s percent:55.05% +[2] userdata: 0x7fc7e0c09970 name:call_func file:[L]@test.lua:37 count:3000000 total:0.222086s ave:0.000000s percent:44.95% +[3] userdata: 0x7fc7e0c099e0 name:foo2 file:[L]@test.lua:62 count:1 total:0.000000s ave:0.000000s percent:0% +[4] userdata: 0x7fc7e0c09a30 name:foo file:[L]@test.lua:68 count:1 total:0.000000s ave:0.000000s percent:0% + +]] + +~~~ diff --git a/framework/lualib-src/lua-profile/icallpath.c b/framework/lualib-src/lua-profile/icallpath.c new file mode 100644 index 0000000..1649c29 --- /dev/null +++ b/framework/lualib-src/lua-profile/icallpath.c @@ -0,0 +1,54 @@ +#include "profile.h" +#include "icallpath.h" +#include "imap.h" + +struct icallpath_context { + uint64_t key; + void* value; + struct imap_context* children; +}; + +struct icallpath_context* icallpath_create(uint64_t key, void* value) { + struct icallpath_context* icallpath = (struct icallpath_context*)pmalloc(sizeof(*icallpath)); + icallpath->key = key; + icallpath->value = value; + icallpath->children = imap_create(); + + return icallpath; +} + +void icallpath_free_child(uint64_t key, void* value, void* ud) { + icallpath_free((struct icallpath_context*)value); +} +void icallpath_free(struct icallpath_context* icallpath) { + if (icallpath->value) { + pfree(icallpath->value); + icallpath->value = NULL; + } + imap_dump(icallpath->children, icallpath_free_child, NULL); + imap_free(icallpath->children); + pfree(icallpath); +} + +struct icallpath_context* icallpath_get_child(struct icallpath_context* icallpath, uint64_t key) { + void* child_path = imap_query(icallpath->children, key); + return (struct icallpath_context*)child_path; +} + +struct icallpath_context* icallpath_add_child(struct icallpath_context* icallpath, uint64_t key, void* value) { + struct icallpath_context* child_path = icallpath_create(key, value); + imap_set(icallpath->children, key, child_path); + return child_path; +} + +void* icallpath_getvalue(struct icallpath_context* icallpath) { + return icallpath->value; +} + +void icallpath_dump_children(struct icallpath_context* icallpath, observer observer_cb, void* ud) { + imap_dump(icallpath->children, observer_cb, ud); +} + +size_t icallpath_children_size(struct icallpath_context* icallpath) { + return imap_size(icallpath->children); +} \ No newline at end of file diff --git a/framework/lualib-src/lua-profile/icallpath.h b/framework/lualib-src/lua-profile/icallpath.h new file mode 100644 index 0000000..e2521eb --- /dev/null +++ b/framework/lualib-src/lua-profile/icallpath.h @@ -0,0 +1,21 @@ +#ifndef _ICALLPATH_H_ +#define _ICALLPATH_H_ + +#include +#include + +struct icallpath_context; + +struct icallpath_context* icallpath_create(uint64_t key, void* value); +void icallpath_free(struct icallpath_context* icallpath); + +struct icallpath_context* icallpath_get_child(struct icallpath_context* icallpath, uint64_t key); +struct icallpath_context* icallpath_add_child(struct icallpath_context* icallpath, uint64_t key, void* value); +void* icallpath_getvalue(struct icallpath_context* icallpath); + +typedef void(*observer)(uint64_t key, void* value, void* ud); +void icallpath_dump_children(struct icallpath_context* icallpath, observer observer_cb, void* ud); +size_t icallpath_children_size(struct icallpath_context* icallpath); + + +#endif diff --git a/framework/lualib-src/lua-profile/imap.c b/framework/lualib-src/lua-profile/imap.c new file mode 100644 index 0000000..1e98b03 --- /dev/null +++ b/framework/lualib-src/lua-profile/imap.c @@ -0,0 +1,188 @@ +#include "imap.h" +#include "profile.h" + +enum imap_status { + IS_NONE, + IS_EXIST, + IS_REMOVE, +}; + +struct imap_slot { + uint64_t key; + void* value; + enum imap_status status; + struct imap_slot* next; +}; + +struct imap_context { + struct imap_slot* slots; + size_t size; + size_t count; + struct imap_slot* lastfree; +}; + +#define DEFAULT_IMAP_SLOT_SIZE 1024 + +struct imap_context * +imap_create() { + struct imap_context* imap = (struct imap_context*)pmalloc(sizeof(*imap)); + imap->slots = (struct imap_slot*)pcalloc(DEFAULT_IMAP_SLOT_SIZE, sizeof(struct imap_slot)); + imap->size = DEFAULT_IMAP_SLOT_SIZE; + imap->count = 0; + imap->lastfree = imap->slots + imap->size; + return imap; +} + + +void +imap_free(struct imap_context* imap) { + pfree(imap->slots); + pfree(imap); +} + + +static inline uint64_t +_imap_hash(struct imap_context* imap, uint64_t key) { + uint64_t hash = key % (uint64_t)(imap->size); + return hash; +} + + +static void +_imap_rehash(struct imap_context* imap) { + size_t new_sz = DEFAULT_IMAP_SLOT_SIZE; + struct imap_slot* old_slots = imap->slots; + size_t old_count = imap->count; + size_t old_size = imap->size; + while(new_sz <= imap->count) { + new_sz *= 2; + } + + struct imap_slot* new_slots = (struct imap_slot*)pcalloc(new_sz, sizeof(struct imap_slot)); + imap->lastfree = new_slots + new_sz; + imap->size = new_sz; + imap->slots = new_slots; + imap->count = 0; + + size_t i=0; + for(i=0; istatus; + if(status == IS_EXIST) { + imap_set(imap, p->key, p->value); + } + } + + assert(old_count == imap->count); + pfree(old_slots); +} + + +static struct imap_slot * +_imap_query(struct imap_context* imap, uint64_t key) { + uint64_t hash = _imap_hash(imap, key); + struct imap_slot* p = &(imap->slots[hash]); + if(p->status != IS_NONE) { + while(p) { + if(p->key == key && p->status == IS_EXIST) { + return p; + } + p = p->next; + } + } + return NULL; +} + + +void * +imap_query(struct imap_context* imap, uint64_t key) { + struct imap_slot* p = _imap_query(imap, key); + if(p) { + return p->value; + } + return NULL; +} + + + +static struct imap_slot * +_imap_getfree(struct imap_context* imap) { + while(imap->lastfree > imap->slots) { + imap->lastfree--; + if(imap->lastfree->status == IS_NONE) { + return imap->lastfree; + } + } + return NULL; +} + + + +void +imap_set(struct imap_context* imap, uint64_t key, void* value) { + assert(value); + uint64_t hash = _imap_hash(imap, key); + struct imap_slot* p = &(imap->slots[hash]); + if(p->status == IS_EXIST) { + struct imap_slot* np = p; + while(np) { + if(np->key == key && np->status == IS_EXIST) { + np->value = value; + return; + } + np = np->next; + } + + np = _imap_getfree(imap); + if(np == NULL) { + _imap_rehash(imap); + imap_set(imap, key, value); + return; + } + + uint64_t main_hash = _imap_hash(imap, p->key); + np->next = p->next; + p->next = np; + if(main_hash == hash) { + p = np; + }else { + np->key = p->key; + np->value = p->value; + np->status = IS_EXIST; + } + } + + imap->count++; + p->status = IS_EXIST; + p->key = key; + p->value = value; +} + + +void * +imap_remove(struct imap_context* imap, uint64_t key) { + struct imap_slot* p = _imap_query(imap, key); + if(p) { + imap->count--; + p->status = IS_REMOVE; + return p->value; + } + return NULL; +} + + +void +imap_dump(struct imap_context* imap, observer observer_cb, void* ud) { + size_t i=0; + for(i=0; isize; i++) { + struct imap_slot* v = &imap->slots[i]; + if(v->status == IS_EXIST) { + observer_cb(v->key, v->value, ud); + } + } +} + +size_t +imap_size(struct imap_context* imap) { + return imap->count; +} \ No newline at end of file diff --git a/framework/lualib-src/lua-profile/imap.h b/framework/lualib-src/lua-profile/imap.h new file mode 100644 index 0000000..b6cc232 --- /dev/null +++ b/framework/lualib-src/lua-profile/imap.h @@ -0,0 +1,24 @@ +#ifndef _IMAP_H_ +#define _IMAP_H_ + + +#include +#include +struct imap_context; + + +struct imap_context* imap_create(); +void imap_free(struct imap_context* imap); + +// the value is no-null point +void imap_set(struct imap_context* imap, uint64_t key, void* value); + +void* imap_remove(struct imap_context* imap, uint64_t key); +void* imap_query(struct imap_context* imap, uint64_t key); + +typedef void(*observer)(uint64_t key, void* value, void* ud); +void imap_dump(struct imap_context* imap, observer observer_cb, void* ud); + +size_t imap_size(struct imap_context* imap); + +#endif diff --git a/framework/lualib-src/lua-profile/profile.c b/framework/lualib-src/lua-profile/profile.c new file mode 100755 index 0000000..0776af7 --- /dev/null +++ b/framework/lualib-src/lua-profile/profile.c @@ -0,0 +1,612 @@ +#include "profile.h" +#include "imap.h" +#include "icallpath.h" +#include "lobject.h" +#include "lstate.h" +#include + +#ifdef USE_GOOGLE_PROFILER + #include +#endif + +#define MAX_CALL_SIZE 1024 +#define MAX_CO_SIZE 1024 +#define NANOSEC 1000000000 +#define MICROSEC 1000000 + +#define KEY "swt_profiler" + +#ifdef USE_RDTSC + #include "rdtsc.h" + static inline uint64_t + gettime() { + return rdtsc(); + } + + static inline double + realtime(uint64_t t) { + return (double) t / (2000000000); + } +#else + static inline uint64_t + gettime() { + struct timespec ti; + // clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ti); + // clock_gettime(CLOCK_MONOTONIC, &ti); + clock_gettime(CLOCK_REALTIME, &ti); // would be faster + + long sec = ti.tv_sec & 0xffff; + long nsec = ti.tv_nsec; + + return sec * NANOSEC + nsec; + } + + static inline double + realtime(uint64_t t) { + return (double)t / NANOSEC; + } +#endif + + +struct callpath_node; +struct call_frame { + const void* point; + const void* prototype; + struct icallpath_context* path; + bool tail; + uint64_t call_time; + uint64_t ret_time; + uint64_t sub_cost; + uint64_t real_cost; + uint64_t alloc_co_cost; + uint64_t alloc_start; +}; + +struct call_state { + lua_State* co; + uint64_t leave_time; + uint64_t leave_alloc; + int top; + struct call_frame call_list[0]; +}; + +struct profile_context { + uint64_t start; + bool increment_alloc_count; + uint64_t alloc_count; + lua_Alloc last_alloc_f; + void* last_alloc_ud; + struct imap_context* cs_map; + struct icallpath_context* callpath; + struct call_state* cur_cs; +}; + +struct callpath_node { + struct callpath_node* parent; + const void* point; + const char* source; + const char* name; + int line; + int depth; + uint64_t ret_time; + uint64_t count; + uint64_t record_time; + uint64_t alloc_count; +}; + +static struct callpath_node* +callpath_node_create() { + struct callpath_node* node = (struct callpath_node*)pmalloc(sizeof(*node)); + node->parent = NULL; + node->point = NULL; + node->source = NULL; + node->name = NULL; + node->line = 0; + node->depth = 0; + node->ret_time = 0; + node->count = 0; + node->record_time = 0; + node->alloc_count = 0; + return node; +} + +uint64_t g_context_ver = 0; +pthread_once_t g_cache_init; +pthread_key_t g_cache_ver; +pthread_key_t g_cache_co; +pthread_key_t g_cache_context; +void cache_init() { + pthread_key_create(&g_cache_ver, NULL); + pthread_key_create(&g_cache_co, NULL); + pthread_key_create(&g_cache_context, NULL); +} +static struct profile_context* get_cache_context(lua_State* L) { + uint64_t v = (uint64_t)((uintptr_t)pthread_getspecific(g_cache_ver)); + if (v != g_context_ver) { + return NULL; + } + lua_State* co = (lua_State*)pthread_getspecific(g_cache_co); + if (co != L) { + return NULL; + } + return (struct profile_context*)pthread_getspecific(g_cache_context); +} +void set_cache_context(lua_State* co, struct profile_context* context) { + pthread_setspecific(g_cache_ver, (const void*)g_context_ver); + pthread_setspecific(g_cache_co, (const void*)co); + pthread_setspecific(g_cache_context, (const void*)context); +} + +static struct profile_context * +profile_create() { + struct profile_context* context = (struct profile_context*)pmalloc(sizeof(*context)); + + context->start = 0; + context->cs_map = imap_create(); + context->callpath = NULL; + context->cur_cs = NULL; + context->increment_alloc_count = false; + context->alloc_count = 0; + context->last_alloc_f = NULL; + context->last_alloc_ud = NULL; + g_context_ver++; + return context; +} + +static void +_ob_free_call_state(uint64_t key, void* value, void* ud) { + pfree(value); +} +static void +profile_free(struct profile_context* context) { + g_context_ver++; + if (context->callpath) { + icallpath_free(context->callpath); + context->callpath = NULL; + } + + imap_dump(context->cs_map, _ob_free_call_state, NULL); + imap_free(context->cs_map); + pfree(context); +} + + +static inline struct call_frame * +push_callframe(struct call_state* cs) { + if(cs->top >= MAX_CALL_SIZE) { + assert(false); + } + return &cs->call_list[cs->top++]; +} + +static inline struct call_frame * +pop_callframe(struct call_state* cs) { + if(cs->top<=0) { + assert(false); + } + return &cs->call_list[--cs->top]; +} + +static inline struct call_frame * +cur_callframe(struct call_state* cs) { + if(cs->top<=0) { + return NULL; + } + + uint64_t idx = cs->top-1; + return &cs->call_list[idx]; +} + + +static inline struct profile_context * +_get_profile(lua_State* L) { + struct profile_context* addr = get_cache_context(L); + if (addr) { + return addr; + } + lua_rawgetp(L, LUA_REGISTRYINDEX, KEY); + addr = (struct profile_context*)lua_touserdata(L, -1); + lua_pop(L, 1); + if (addr) { + set_cache_context(L, addr); + } + return addr; +} + +static struct icallpath_context* +get_frame_path(struct profile_context* context, lua_State* co, lua_Debug* far, struct icallpath_context* pre_callpath, struct call_frame* frame) { + if (!context->callpath) { + struct callpath_node* node = callpath_node_create(); + node->name = "total"; + node->source = node->name; + context->callpath = icallpath_create(0, node); + } + struct icallpath_context* path = pre_callpath; + if (!path) { + path = context->callpath; + } + + struct call_frame* cur_cf = frame; + uint64_t k = (uint64_t)((uintptr_t)cur_cf->prototype); + struct icallpath_context* child_path = icallpath_get_child(path, k); + if (!child_path) { + struct callpath_node* path_parent = (struct callpath_node*)icallpath_getvalue(path); + struct callpath_node* node = callpath_node_create(); + + node->parent = path_parent; + node->point = cur_cf->prototype; + node->depth = path_parent->depth + 1; + node->ret_time = 0; + node->record_time = 0; + node->count = 0; + node->alloc_count = 0; + child_path = icallpath_add_child(path, k, node); + } + path = child_path; + + struct callpath_node* cur_node = (struct callpath_node*)icallpath_getvalue(path); + if (cur_node->name == NULL) { + const char* name = NULL; + #ifdef USE_EXPORT_NAME + lua_getinfo(co, "nSl", far); + name = far->name; + #else + lua_getinfo(co, "Sl", far); + #endif + int line = far->linedefined; + const char* source = far->source; + char flag = far->what[0]; + if (flag == 'C') { + lua_Debug ar2; + int i=0; + int ret = 0; + do { + i++; + ret = lua_getstack(co, i, &ar2); + flag = 'C'; + if(ret) { + lua_getinfo(co, "Sl", &ar2); + if(ar2.what[0] != 'C') { + line = ar2.currentline; + source = ar2.source; + break; + } + } + }while(ret); + } + + cur_node->name = name ? name : "null"; + cur_node->source = source ? source : "null"; + cur_node->line = line; + } + + return path; +} + +static void* +_resolve_alloc(void *ud, void *ptr, size_t osize, size_t nsize) { + struct profile_context* context = (struct profile_context*)ud; + size_t old = ptr == NULL ? 0 : osize; + if (nsize > 0 && nsize > old && context->increment_alloc_count) { + context->alloc_count += (nsize - old); + } + + void* p = context->last_alloc_f(context->last_alloc_ud, ptr, osize, nsize); + return p; +} + +static void +_resolve_hook(lua_State* L, lua_Debug* far) { + struct profile_context* context = _get_profile(L); + if(context->start == 0) { + return; + } + + uint64_t cur_time = gettime(); + context->increment_alloc_count = false; + int event = far->event; + struct call_state* cs = context->cur_cs; + if (!context->cur_cs || context->cur_cs->co != L) { + uint64_t key = (uint64_t)((uintptr_t)L); + cs = imap_query(context->cs_map, key); + if (cs == NULL) { + cs = (struct call_state*)pmalloc(sizeof(struct call_state) + sizeof(struct call_frame)*MAX_CALL_SIZE); + cs->co = L; + cs->top = 0; + cs->leave_time = 0; + cs->leave_alloc = 0; + imap_set(context->cs_map, key, cs); + } + + if (context->cur_cs) { + context->cur_cs->leave_time = cur_time; + context->cur_cs->leave_alloc = context->alloc_count; + } + context->cur_cs = cs; + } + if (cs->leave_time > 0) { + assert(cur_time >= cs->leave_time); + uint64_t co_cost = cur_time - cs->leave_time; + uint64_t co_alloc = context->alloc_count - cs->leave_alloc; + + int i = 0; + for (; i < cs->top; i++) { + cs->call_list[i].sub_cost += co_cost; + cs->call_list[i].alloc_co_cost += co_alloc; + } + cs->leave_time = 0; + cs->leave_alloc = 0; + } + assert(cs->co == L); + + if (event == LUA_HOOKCALL || event == LUA_HOOKTAILCALL) { + const void* point = NULL; + if (far->i_ci && far->i_ci->func) { + point = far->i_ci->func; + } else { + lua_getinfo(L, "f", far); + point = lua_topointer(L, -1); + } + + struct icallpath_context* pre_callpath = NULL; + struct call_frame* pre_frame = cur_callframe(cs); + if (pre_frame) { + pre_callpath = pre_frame->path; + } + + struct call_frame* frame = push_callframe(cs); + frame->point = point; + frame->tail = event == LUA_HOOKTAILCALL; + frame->sub_cost = 0; + frame->call_time = cur_time; + frame->alloc_co_cost = 0; + frame->alloc_start = context->alloc_count; + frame->prototype = point; + if (far->i_ci && ttisclosure(s2v(far->i_ci->func))) { + Closure *cl = clvalue(s2v(far->i_ci->func)); + if (cl && cl->c.tt == LUA_VLCL) { + frame->prototype = cl->l.p; + } + } + frame->path = get_frame_path(context, L, far, pre_callpath, frame); + } else if (event == LUA_HOOKRET) { + int len = cs->top; + if (len <= 0) { + context->increment_alloc_count = true; + return; + } + bool tail_call = false; + do { + struct call_frame* cur_frame = pop_callframe(cs); + struct callpath_node* cur_path = (struct callpath_node*)icallpath_getvalue(cur_frame->path); + uint64_t total_cost = cur_time - cur_frame->call_time; + uint64_t real_cost = total_cost - cur_frame->sub_cost; + uint64_t alloc_count = context->alloc_count - cur_frame->alloc_start - cur_frame->alloc_co_cost; + assert(context->alloc_count >= (cur_frame->alloc_start + cur_frame->alloc_co_cost)); + assert(cur_time >= cur_frame->call_time && total_cost >= cur_frame->sub_cost); + cur_frame->ret_time = cur_time; + cur_frame->real_cost = real_cost; + + cur_path->ret_time = cur_path->ret_time == 0 ? cur_time : cur_path->ret_time; + cur_path->record_time += real_cost; + cur_path->count++; + cur_path->alloc_count += alloc_count; + + struct call_frame* pre_frame = cur_callframe(cs); + tail_call = pre_frame ? cur_frame->tail : false; + }while(tail_call); + } + + context->increment_alloc_count = true; +} + + +struct dump_call_path_arg { + lua_State* L; + uint64_t record_time; + uint64_t count; + uint64_t index; + uint64_t alloc_count; +}; + +static void _dump_call_path(struct icallpath_context* path, struct dump_call_path_arg* arg); +static void _dump_call_path_child(uint64_t key, void* value, void* ud) { + struct dump_call_path_arg* arg = (struct dump_call_path_arg*)ud; + _dump_call_path((struct icallpath_context*)value, arg); + lua_seti(arg->L, -2, ++arg->index); +} +static void _dump_call_path(struct icallpath_context* path, struct dump_call_path_arg* arg) { + lua_checkstack(arg->L, 3); + lua_newtable(arg->L); + + struct dump_call_path_arg child_arg; + child_arg.L = arg->L; + child_arg.record_time = 0; + child_arg.count = 0; + child_arg.index = 0; + child_arg.alloc_count = 0; + + if (icallpath_children_size(path) > 0) { + lua_newtable(arg->L); + icallpath_dump_children(path, _dump_call_path_child, &child_arg); + lua_setfield(arg->L, -2, "children"); + } + + struct callpath_node* node = (struct callpath_node*)icallpath_getvalue(path); + uint64_t alloc_count = node->alloc_count > child_arg.alloc_count ? node->alloc_count : child_arg.alloc_count; + uint64_t count = node->count > child_arg.count ? node->count : child_arg.count; + uint64_t rt = realtime(node->record_time) * MICROSEC; + uint64_t record_time = rt > child_arg.record_time ? rt : child_arg.record_time; + + arg->record_time += record_time; + arg->count += count; + arg->alloc_count += alloc_count; + + char name[512] = {0}; + snprintf(name, sizeof(name)-1, "%s %s:%d", node->name ? node->name : "", node->source ? node->source : "", node->line); + lua_pushstring(arg->L, name); + lua_setfield(arg->L, -2, "name"); + + lua_pushinteger(arg->L, count); + lua_setfield(arg->L, -2, "count"); + + lua_pushinteger(arg->L, record_time); + lua_setfield(arg->L, -2, "value"); + + lua_pushinteger(arg->L, node->ret_time); + lua_setfield(arg->L, -2, "rettime"); + + lua_pushinteger(arg->L, alloc_count); + lua_setfield(arg->L, -2, "alloc_count"); +} + +static void dump_call_path(lua_State* L, struct icallpath_context* path) { + struct dump_call_path_arg arg; + arg.L = L; + arg.record_time = 0; + arg.count = 0; + arg.index = 0; + arg.alloc_count = 0; + _dump_call_path(path, &arg); +} + +static int +get_all_coroutines(lua_State* L, lua_State** result, int maxsize) { + int i = 0; + struct global_State* lG = L->l_G; + result[i++] = lG->mainthread; + + struct GCObject* obj = lG->allgc; + while (obj && i < maxsize) { + if (obj->tt == LUA_TTHREAD) { + result[i++] = gco2th(obj); + } + obj = obj->next; + } + return i; +} + +static int +_lstart(lua_State* L) { + struct profile_context* context = _get_profile(L); + if (context) { + return 0; + } + #ifdef USE_GOOGLE_PROFILER + printf("\n==============nProfilerStart================\n"); + ProfilerStart("log/swt.prof"); + #endif + + // init registry + context = profile_create(); + + lua_pushlightuserdata(L, context); + lua_rawsetp(L, LUA_REGISTRYINDEX, KEY); + context->start = gettime(); + context->last_alloc_f = lua_getallocf(L, &context->last_alloc_ud); + lua_setallocf(L, _resolve_alloc, (void*)context); + + lua_State* states[MAX_CO_SIZE] = {0}; + int i = get_all_coroutines(L, states, MAX_CO_SIZE); + for (i = i - 1; i >= 0; i--) { + lua_sethook(states[i], _resolve_hook, LUA_MASKCALL | LUA_MASKRET, 0); + } + context->increment_alloc_count = true; + return 0; +} + +static int +_lstop(lua_State* L) { + struct profile_context* context = _get_profile(L); + if (!context) { + return 0; + } + + context->increment_alloc_count = false; + + void* current_ud = NULL; + lua_getallocf(L, ¤t_ud); + if (current_ud == context) { + lua_setallocf(L, context->last_alloc_f, context->last_alloc_ud); + } + + lua_State* states[MAX_CO_SIZE] = {0}; + int i = get_all_coroutines(L, states, MAX_CO_SIZE); + for (i = i - 1; i >= 0; i--) { + lua_sethook(states[i], NULL, 0, 0); + } + profile_free(context); + + lua_pushlightuserdata(L, KEY); + lua_pushnil(L); + lua_settable(L, LUA_REGISTRYINDEX); + + #ifdef USE_GOOGLE_PROFILER + printf("\n==============ProfilerStop================\n"); + ProfilerStop(); + #endif + + return 0; +} + +static int +_lmark(lua_State* L) { + struct profile_context* context = _get_profile(L); + if (!context) { + return 0; + } + lua_State* co = lua_tothread(L, 1); + if(co == NULL) { + co = L; + } + if(context->start != 0) { + lua_sethook(co, _resolve_hook, LUA_MASKCALL | LUA_MASKRET, 0); + } + lua_pushboolean(L, context->start != 0); + return 1; +} + +static int +_lunmark(lua_State* L) { + struct profile_context* context = _get_profile(L); + if (!context) { + return 0; + } + lua_State* co = lua_tothread(L, 1); + if(co == NULL) { + co = L; + } + lua_sethook(co, NULL, 0, 0); + return 0; +} + +static int +_ldump(lua_State* L) { + struct profile_context* context = _get_profile(L); + if (context && context->callpath) { + context->increment_alloc_count = false; + uint64_t record_time = realtime(gettime() - context->start) * MICROSEC; + lua_pushinteger(L, record_time); + dump_call_path(L, context->callpath); + context->increment_alloc_count = true; + return 2; + } + return 0; +} + +int +luaopen_profile_c(lua_State* L) { + pthread_once(&g_cache_init, cache_init); + + luaL_checkversion(L); + luaL_Reg l[] = { + {"start", _lstart}, + {"stop", _lstop}, + {"mark", _lmark}, + {"unmark", _lunmark}, + {"dump", _ldump}, + {NULL, NULL}, + }; + luaL_newlib(L, l); + return 1; +} \ No newline at end of file diff --git a/framework/lualib-src/lua-profile/profile.h b/framework/lualib-src/lua-profile/profile.h new file mode 100644 index 0000000..cbb4214 --- /dev/null +++ b/framework/lualib-src/lua-profile/profile.h @@ -0,0 +1,20 @@ +#ifndef _PROFILE_H_ +#define _PROFILE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define prealloc realloc +#define pmalloc malloc +#define pfree free +#define pcalloc calloc + +#endif \ No newline at end of file diff --git a/framework/lualib-src/lua-profile/rdtsc.h b/framework/lualib-src/lua-profile/rdtsc.h new file mode 100644 index 0000000..aedf946 --- /dev/null +++ b/framework/lualib-src/lua-profile/rdtsc.h @@ -0,0 +1,55 @@ +#ifndef __RDTSC_H_DEFINED__ +#define __RDTSC_H_DEFINED__ + + +#if defined(__i386__) + +static __inline__ unsigned long long rdtsc(void) +{ + unsigned long long int x; + __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); + return x; +} +#elif defined(__x86_64__) + +static __inline__ unsigned long long rdtsc(void) +{ + unsigned hi, lo; + __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); + return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 ); +} + +#elif defined(__powerpc__) + +static __inline__ unsigned long long rdtsc(void) +{ + unsigned long long int result=0; + unsigned long int upper, lower,tmp; + __asm__ volatile( + "0: \n" + "\tmftbu %0 \n" + "\tmftb %1 \n" + "\tmftbu %2 \n" + "\tcmpw %2,%0 \n" + "\tbne 0b \n" + : "=r"(upper),"=r"(lower),"=r"(tmp) + ); + result = upper; + result = result<<32; + result = result|lower; + + return(result); +} + +#else + +#error "No tick counter is available!" + +#endif + + +/* $RCSfile: $ $Author: kazutomo $ + * $Revision: 1.6 $ $Date: 2005/04/13 18:49:58 $ + */ + +#endif \ No newline at end of file diff --git a/framework/lualib-src/lua-snapshot/Makefile b/framework/lualib-src/lua-snapshot/Makefile new file mode 100644 index 0000000..437910c --- /dev/null +++ b/framework/lualib-src/lua-snapshot/Makefile @@ -0,0 +1,30 @@ +SKYNET_ROOT ?= ../../skynet +include $(SKYNET_ROOT)/platform.mk + +PLAT ?= none + +TARGET = ../../luaclib/snapshot.so + +ifeq ($(PLAT), macosx) + CFLAGS = -g -O2 -dynamiclib -Wl,-undefined,dynamic_lookup +else +ifeq ($(PLAT), linux) + CFLAGS = -g -O2 -shared -fPIC +endif +endif + +LUA_LIB ?= $(SKYNET_ROOT)/3rd/lua/ +LUA_INC ?= $(SKYNET_ROOT)/3rd/lua/ +SKYNET_SRC ?= $(SKYNET_ROOT)/skynet-src + +SRC = . + +.PHONY: all clean + +all: $(TARGET) + +$(TARGET): $(foreach dir, $(SRC), $(wildcard $(dir)/*.c)) + $(CC) $(CFLAGS) $(SHARED) -I$(LUA_INC) -I$(SKYNET_SRC) $^ -o $@ + +clean: + rm -f *.o $(TARGET) \ No newline at end of file diff --git a/framework/lualib-src/lua-snapshot/snapshot.c b/framework/lualib-src/lua-snapshot/snapshot.c new file mode 100644 index 0000000..0c83972 --- /dev/null +++ b/framework/lualib-src/lua-snapshot/snapshot.c @@ -0,0 +1,652 @@ +#include +#include +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "lfunc.h" + +struct snapshot_params { + int max_count; + int current_mark_count; +}; + +static void mark_object(lua_State *L, lua_State *dL, const void * parent, const char * desc, struct snapshot_params* args); + +#define check_limit(L, args) do { \ + if((args)->max_count > 0) { \ + if(((args)->current_mark_count)++ > (args)->max_count) { \ + lua_pop((L),1); \ + return; \ + } \ + } \ +} while(0); + +#if LUA_VERSION_NUM == 501 + +static void +luaL_checkversion(lua_State *L) { + if (lua_pushthread(L) == 0) { + luaL_error(L, "Must require in main thread"); + } + lua_setfield(L, LUA_REGISTRYINDEX, "mainthread"); +} + +static void +lua_rawsetp(lua_State *L, int idx, const void *p) { + if (idx < 0) { + idx += lua_gettop(L) + 1; + } + lua_pushlightuserdata(L, (void *)p); + lua_insert(L, -2); + lua_rawset(L, idx); +} + +static void +lua_rawgetp(lua_State *L, int idx, const void *p) { + if (idx < 0) { + idx += lua_gettop(L) + 1; + } + lua_pushlightuserdata(L, (void *)p); + lua_rawget(L, idx); +} + +static void +lua_getuservalue(lua_State *L, int idx) { + lua_getfenv(L, idx); +} + +static void +mark_function_env(lua_State *L, lua_State *dL, const void * t, struct snapshot_params* args) { + lua_getfenv(L,-1); + if (lua_istable(L,-1)) { + mark_object(L, dL, t, "[environment]"); + } else { + lua_pop(L,1); + } +} + +// lua 5.1 has no light c function +#define is_lightcfunction(L, idx) (0) + +#else + +#define mark_function_env(L,dL,t,args) + +static int +is_lightcfunction(lua_State *L, int idx) { + if (lua_iscfunction(L, idx)) { + if (lua_getupvalue(L, idx, 1) == NULL) { + return 1; + } + lua_pop(L, 1); + } + return 0; +} + +#endif + +#if LUA_VERSION_NUM == 504 + /* + ** True if value of 'alimit' is equal to the real size of the array + ** part of table 't'. (Otherwise, the array part must be larger than + ** 'alimit'.) + */ + #define limitequalsasize(t) (isrealasize(t) || ispow2((t)->alimit)) + + + /* + ** Returns the real size of the 'array' array + */ + static unsigned int _luaH_realasize (const Table *t) { + if (limitequalsasize(t)) + return t->alimit; /* this is the size */ + else { + unsigned int size = t->alimit; + /* compute the smallest power of 2 not smaller than 'n' */ + size |= (size >> 1); + size |= (size >> 2); + size |= (size >> 4); + size |= (size >> 8); + size |= (size >> 16); + #if (UINT_MAX >> 30) > 3 + size |= (size >> 32); /* unsigned int has more than 32 bits */ + #endif + size++; + lua_assert(ispow2(size) && size/2 < t->alimit && t->alimit < size); + return size; + } + } +#endif + +#include +#include +#include + +#define TABLE 1 +#define FUNCTION 2 +#define SOURCE 3 +#define THREAD 4 +#define USERDATA 5 +#define STRING 6 +#define MARK 7 + +static bool +ismarked(lua_State *dL, const void *p) { + lua_rawgetp(dL, MARK, p); + if (lua_isnil(dL,-1)) { + lua_pop(dL,1); + lua_pushboolean(dL,1); + lua_rawsetp(dL, MARK, p); + return false; + } + lua_pop(dL,1); + return true; +} + +static const void * +readobject(lua_State *L, lua_State *dL, const void *parent, const char *desc) { + int t = lua_type(L, -1); + int tidx = 0; + switch (t) { + case LUA_TTABLE: + tidx = TABLE; + break; + case LUA_TFUNCTION: + if (is_lightcfunction(L, -1)) { + lua_pop(L, 1); + return NULL; + } + tidx = FUNCTION; + break; + case LUA_TTHREAD: + tidx = THREAD; + break; + case LUA_TUSERDATA: + tidx = USERDATA; + break; +#ifdef DUMP_STRING + case LUA_TSTRING: + tidx = STRING; + break; +#endif + default: + lua_pop(L, 1); + return NULL; + } + + const void * p = NULL; + if(t == LUA_TUSERDATA) { + const TValue *o = NULL; + #if LUA_VERSION_NUM == 504 + o = s2v(L->top - 1); + #else + o = L->top - 1; + #endif + // const TValue *o = index2value(L, -1); + p = (const void*)uvalue(o); + } else if (t == LUA_TSTRING) { + p = lua_tostring(L, -1); + } + else { + p = (const void*)lua_topointer(L, -1); + } + if (ismarked(dL, p)) { + lua_rawgetp(dL, tidx, p); + if (!lua_isnil(dL,-1)) { + lua_pushstring(dL,desc); + lua_rawsetp(dL, -2, parent); + } + lua_pop(dL,1); + lua_pop(L,1); + return NULL; + } + + lua_newtable(dL); + lua_pushstring(dL,desc); + lua_rawsetp(dL, -2, parent); + lua_rawsetp(dL, tidx, p); + + return p; +} + +static const char * +keystring(lua_State *L, int index, char * buffer) { + int t = lua_type(L,index); + switch (t) { + case LUA_TSTRING: + return lua_tostring(L,index); + case LUA_TNUMBER: + sprintf(buffer,"[%lg]",lua_tonumber(L,index)); + break; + case LUA_TBOOLEAN: + sprintf(buffer,"[%s]",lua_toboolean(L,index) ? "true" : "false"); + break; + case LUA_TNIL: + sprintf(buffer,"[nil]"); + break; + default: + sprintf(buffer,"[%s:%p]",lua_typename(L,t),lua_topointer(L,index)); + break; + } + return buffer; +} + +static void +mark_table(lua_State *L, lua_State *dL, const void * parent, const char * desc, struct snapshot_params* args) { + const void * t = readobject(L, dL, parent, desc); + if (t == NULL) + return; + + check_limit(L, args); + + bool weakk = false; + bool weakv = false; + if (lua_getmetatable(L, -1)) { + lua_pushliteral(L, "__mode"); + lua_rawget(L, -2); + if (lua_isstring(L,-1)) { + const char *mode = lua_tostring(L, -1); + if (strchr(mode, 'k')) { + weakk = true; + } + if (strchr(mode, 'v')) { + weakv = true; + } + } + lua_pop(L,1); + + luaL_checkstack(L, LUA_MINSTACK, NULL); + mark_table(L, dL, t, "[metatable]", args); + } + + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if (weakv) { + lua_pop(L,1); + } else { + char temp[32]; + const char * desc = keystring(L, -2, temp); + mark_object(L, dL, t , desc, args); + } + if (!weakk) { + lua_pushvalue(L,-1); + mark_object(L, dL, t , "[key]", args); + } + } + + lua_pop(L,1); +} + +static void +mark_string(lua_State *L, lua_State *dL, const void * parent, const char *desc) { + const void* t = readobject(L, dL, parent, desc); + if(t == NULL) + return; + lua_pop(L,1); +} + +static void +mark_userdata(lua_State *L, lua_State *dL, const void * parent, const char *desc, struct snapshot_params* args) { + const void * t = readobject(L, dL, parent, desc); + if (t == NULL) + return; + + check_limit(L, args); + + if (lua_getmetatable(L, -1)) { + mark_table(L, dL, t, "[metatable]", args); + } + + lua_getuservalue(L,-1); + if (lua_isnil(L,-1)) { + lua_pop(L,2); + } else { + mark_object(L, dL, t, "[uservalue]", args); + lua_pop(L,1); + } +} + +static void +mark_function(lua_State *L, lua_State *dL, const void * parent, const char *desc, struct snapshot_params* args) { + const void * t = readobject(L, dL, parent, desc); + if (t == NULL) + return; + + check_limit(L, args); + + mark_function_env(L,dL,t, args); + int i; + for (i=1;;i++) { + const char *name = lua_getupvalue(L,-1,i); + if (name == NULL) + break; + mark_object(L, dL, t, name[0] ? name : "[upvalue]", args); + } + if (lua_iscfunction(L,-1)) { + lua_pop(L,1); + } else { + lua_Debug ar; + lua_getinfo(L, ">S", &ar); + luaL_Buffer b; + luaL_buffinit(dL, &b); + luaL_addstring(&b, ar.short_src); + char tmp[16]; + sprintf(tmp,":%d",ar.linedefined); + luaL_addstring(&b, tmp); + luaL_pushresult(&b); + lua_rawsetp(dL, SOURCE, t); + } +} + +static void +mark_thread(lua_State *L, lua_State *dL, const void * parent, const char *desc, struct snapshot_params* args) { + const void * t = readobject(L, dL, parent, desc); + if (t == NULL) + return; + + check_limit(L, args); + + int level = 0; + lua_State *cL = lua_tothread(L,-1); + if (cL == L) { + level = 1; + } else { + // mark stack + int top = lua_gettop(cL); + luaL_checkstack(cL, 1, NULL); + int i; + char tmp[16]; + for (i=0;i=0) { + char tmp[16]; + sprintf(tmp,":%d ",ar.currentline); + luaL_addstring(&b, tmp); + } + + int i,j; + for (j=1;j>-1;j-=2) { + for (i=j;;i+=j) { + const char * name = lua_getlocal(cL, &ar, i); + if (name == NULL) + break; + snprintf(tmp, sizeof(tmp), "%s{#%s:%d}",name,ar.short_src,ar.linedefined); + mark_object(cL, dL, t, tmp, args); + } + } + + ++level; + } + luaL_pushresult(&b); + lua_rawsetp(dL, SOURCE, t); + lua_pop(L,1); +} + +static void +mark_object(lua_State *L, lua_State *dL, const void * parent, const char *desc, struct snapshot_params* args) { + luaL_checkstack(L, LUA_MINSTACK, NULL); + int t = lua_type(L, -1); + switch (t) { + case LUA_TTABLE: + mark_table(L, dL, parent, desc, args); + break; + case LUA_TUSERDATA: + mark_userdata(L, dL, parent, desc, args); + break; + case LUA_TFUNCTION: + mark_function(L, dL, parent, desc, args); + break; + case LUA_TTHREAD: + mark_thread(L, dL, parent, desc, args); + break; + case LUA_TSTRING: + mark_string(L, dL, parent, desc); + break; + default: + lua_pop(L,1); + break; + } +} + +static int +count_table(lua_State *L, int idx) { + int n = 0; + lua_pushnil(L); + while (lua_next(L, idx) != 0) { + ++n; + lua_pop(L,1); + } + return n; +} + +static void +gen_table_desc(lua_State *dL, luaL_Buffer *b, const void * parent, const char *desc) { + char tmp[32]; + size_t l = sprintf(tmp,"%p : ",parent); + luaL_addlstring(b, tmp, l); + luaL_addstring(b, desc); + luaL_addchar(b, '\n'); +} + +static size_t +_table_size(Table* p) { + size_t size = sizeof(*p); + size_t tl = (p->lastfree == NULL)?(0):(sizenode(p)); + size += tl*sizeof(Node); + #if LUA_VERSION_NUM == 504 + size += _luaH_realasize(p)*sizeof(TValue); + #else + size += p->sizearray*sizeof(TValue); + #endif + return size; +} + +static size_t +_thread_size(struct lua_State* p) { + size_t size = sizeof(*p); + size += p->nci*sizeof(CallInfo); + #ifdef stacksize + size += stacksize(p)*sizeof(*p->stack); + #else + size += p->stacksize*sizeof(*p->stack); + #endif + return size; +} + + +static size_t +_userdata_size(Udata *p) { + #if LUA_VERSION_NUM == 504 + int size = sizeudata(p->nuvalue, p->len); + return size; + #else + int l = sizeudata(p); + size_t size = sizeludata(l); + return size; + #endif +} + +static size_t +_cfunc_size(CClosure* p) { + int n = (int)(p->nupvalues); + size_t size = sizeCclosure(n); + return size; +} + + +static size_t +_lfunc_size(LClosure *p) { + int n = (int)(p->nupvalues); + size_t size = sizeLclosure(n); + return size; +} + +static void +pdesc(lua_State *L, lua_State *dL, int idx, const char * typename) { + lua_pushnil(dL); + size_t size = 0; + char buff_sz[128] = {0}; + while (lua_next(dL, idx) != 0) { + luaL_Buffer b; + luaL_buffinit(L, &b); + const void * key = lua_touserdata(dL, -2); + switch(idx) { + case FUNCTION: { + lua_rawgetp(dL, SOURCE, key); + if (lua_isnil(dL, -1)) { + size = _cfunc_size((CClosure*)key); + snprintf(buff_sz, sizeof(buff_sz), "{%zd}", size); + luaL_addstring(&b,"cfunction"); + luaL_addchar(&b,' '); + luaL_addstring(&b, buff_sz); + luaL_addchar(&b,'\n'); + } else { + size_t l = 0; + size = _lfunc_size((LClosure*)key); + snprintf(buff_sz, sizeof(buff_sz), "{%zd}", size); + const char * s = lua_tolstring(dL, -1, &l); + if(l==0) { + s = "?"; + l = 1; + } + luaL_addlstring(&b,s,l); + luaL_addchar(&b,' '); + luaL_addstring(&b, buff_sz); + luaL_addchar(&b,'\n'); + } + lua_pop(dL, 1); + }break; + + case THREAD: { + lua_rawgetp(dL, SOURCE, key); + size_t l = 0; + size = _thread_size((struct lua_State*)key); + snprintf(buff_sz, sizeof(buff_sz), "{%zd}", size); + const char * s = lua_tolstring(dL, -1, &l); + luaL_addstring(&b,"thread "); + luaL_addlstring(&b,s,l); + luaL_addchar(&b,' '); + luaL_addstring(&b, buff_sz); + luaL_addchar(&b,'\n'); + lua_pop(dL, 1); + }break; + + case TABLE: { + size = _table_size((Table*)key); + snprintf(buff_sz, sizeof(buff_sz), "{%zd}", size); + luaL_addstring(&b, typename); + luaL_addchar(&b,' '); + luaL_addstring(&b, buff_sz); + luaL_addchar(&b,'\n'); + }break; + + case USERDATA: { + Udata* p = (Udata*)key; + size = _userdata_size(p); + snprintf(buff_sz, sizeof(buff_sz), "{%zd}", size); + luaL_addstring(&b, typename); + luaL_addchar(&b,' '); + luaL_addstring(&b, buff_sz); + luaL_addchar(&b,'\n'); + }break; + + case STRING: { + #if LUA_VERSION_NUM == 504 + TString* ts = (TString*)(((char*)key) - offsetof(TString, contents)); + #else + TString* ts = (TString*)(((char*)key) - sizeof(UTString)); + #endif + size = tsslen(ts); + snprintf(buff_sz, sizeof(buff_sz), "{%zd}", size); + luaL_addstring(&b, typename); + luaL_addchar(&b,' '); + luaL_addstring(&b, buff_sz); + luaL_addchar(&b,'\n'); + }break; + + default: { + snprintf(buff_sz, sizeof(buff_sz), "{0}"); + luaL_addstring(&b, typename); + luaL_addchar(&b,' '); + luaL_addstring(&b, buff_sz); + luaL_addchar(&b,'\n'); + }break; + } + lua_pushnil(dL); + while (lua_next(dL, -2) != 0) { + const void * parent = lua_touserdata(dL,-2); + const char * desc = luaL_checkstring(dL,-1); + gen_table_desc(dL, &b, parent, desc); + lua_pop(dL,1); + } + luaL_pushresult(&b); + lua_rawsetp(L, -2, key); + lua_pop(dL,1); + } +} + +static void +gen_result(lua_State *L, lua_State *dL) { + int count = 0; + count += count_table(dL, TABLE); + count += count_table(dL, FUNCTION); + count += count_table(dL, USERDATA); + count += count_table(dL, THREAD); + count += count_table(dL, STRING); + lua_createtable(L, 0, count); + pdesc(L, dL, TABLE, "table"); + pdesc(L, dL, USERDATA, "userdata"); + pdesc(L, dL, FUNCTION, "function"); + pdesc(L, dL, THREAD, "thread"); + pdesc(L, dL, STRING, "string"); +} + +static int +snapshot(lua_State *L) { + int i; + struct snapshot_params args = {0}; + args.max_count = luaL_optinteger(L, 1, 0); + lua_State *dL = luaL_newstate(); + for (i=0;i +#include +#include +#include +#include + +#if defined(__APPLE__) +#include +#include +#include +#include +#endif + +#include "lua.h" +#include "lauxlib.h" + +#include "skynet_malloc.h" + +typedef void (*timer_execute_func)(void *ud, void *arg); + +#define TIME_NEAR_SHIFT 8 +#define TIME_NEAR (1 << TIME_NEAR_SHIFT) +#define TIME_LEVEL_SHIFT 6 +#define TIME_LEVEL (1 << TIME_LEVEL_SHIFT) +#define TIME_NEAR_MASK (TIME_NEAR - 1) +#define TIME_LEVEL_MASK (TIME_LEVEL - 1) + +struct timer_event +{ + short rear; + int64_t level; + int64_t near; +}; + +struct timer_node +{ + struct timer_node *next; + uint32_t expire; + uint64_t id; +}; + +struct link_list +{ + struct timer_node head; + struct timer_node *tail; +}; + +struct timer +{ + struct link_list near[TIME_NEAR]; + struct link_list t[4][TIME_LEVEL]; + uint32_t time; + uint32_t starttime; + uint64_t current; + uint64_t current_point; + uint64_t cnt; +}; + +static inline struct timer_node * +link_clear(struct link_list *list) +{ + struct timer_node *ret = list->head.next; + list->head.next = 0; + list->tail = &(list->head); + + return ret; +} + +static inline void +link(struct link_list *list, struct timer_node *node) +{ + list->tail->next = node; + list->tail = node; + node->next = 0; +} + +static inline void +add_node(struct timer *T, struct timer_node *node) +{ + struct timer_event *tevent = (struct timer_event *)(node + 1); + // 添加节点 + uint32_t time = node->expire; + uint32_t current_time = T->time; + + if ((time | TIME_NEAR_MASK) == (current_time | TIME_NEAR_MASK)) + { + link(&T->near[time & TIME_NEAR_MASK], node); + tevent->near = time & TIME_NEAR_MASK; + // printf("near ----%d,%d,%d,%d\n",time,current_time,time|TIME_NEAR_MASK,current_time|TIME_NEAR_MASK); + } + else + { + int i; + uint32_t mask = TIME_NEAR << TIME_LEVEL_SHIFT; + for (i = 0; i < 3; i++) + { + if ((time | (mask - 1)) == (current_time | (mask - 1))) + { + break; + } + mask <<= TIME_LEVEL_SHIFT; + } + int64_t level = (time >> (TIME_NEAR_SHIFT + i * TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK; + tevent->rear = i; + tevent->level = level; + // printf("REAR ----%d,%d,| %d,%d\n",time,current_time,level,i); + link(&T->t[i][level], node); + } +} + +static inline void * +timer_add(struct timer *T, void *arg, size_t sz, int time) +{ + struct timer_node *node = (struct timer_node *)skynet_malloc(sizeof(*node) + sz); + memcpy(node + 1, arg, sz); + node->expire = time + T->time; + // printf(" add == %d, t->time=%d , expire =%d\n",time,T->time,node->expire); + add_node(T, node); + return node; +} + +static inline void +move_list(struct timer *T, int level, int idx) +{ + struct timer_node *current = link_clear(&T->t[level][idx]); + while (current) + { + struct timer_node *temp = current->next; + add_node(T, current); + current = temp; + } +} + +static inline void +timer_shift(struct timer *T) +{ + int mask = TIME_NEAR; + uint32_t ct = ++T->time; + if (ct == 0) + { + move_list(T, 3, 0); + } + else + { + uint32_t time = ct >> TIME_NEAR_SHIFT; + int i = 0; + + while ((ct & (mask - 1)) == 0) + { + int idx = time & TIME_LEVEL_MASK; + if (idx != 0) + { + move_list(T, i, idx); + break; + } + mask <<= TIME_LEVEL_SHIFT; + time >>= TIME_LEVEL_SHIFT; + ++i; + } + } +} + +static inline void +dispatch_list(lua_State *L, struct timer_node *current, int *tidx) +{ + do + { + // 获取数据 + *tidx = *tidx + 1; + lua_pushinteger(L, current->id); + lua_rawseti(L, -2, *tidx); + // printf("dispatch tidx=%d=%d\n ",*tidx,current->id); + + struct timer_node *temp = current; + current = current->next; + skynet_free(temp); + } while (current); +} + +static inline void +timer_execute(lua_State *L, struct timer *T, short *hasdata, int *tidx) +{ + int idx = T->time & TIME_NEAR_MASK; + // printf("exectue ==%d,idx=%d \n",T->time,idx); + while (T->near[idx].head.next) + { + if (*hasdata == 0) + { + *hasdata = 1; + lua_newtable(L); + } + struct timer_node *current = link_clear(&T->near[idx]); + dispatch_list(L, current, tidx); + } +} + +static inline struct timer * +timer_create_timer() +{ + struct timer *r = (struct timer *)skynet_malloc(sizeof(struct timer)); + memset(r, 0, sizeof(*r)); + + int i, j; + + for (i = 0; i < TIME_NEAR; i++) + { + link_clear(&r->near[i]); + } + + for (i = 0; i < 4; i++) + { + for (j = 0; j < TIME_LEVEL; j++) + { + link_clear(&r->t[i][j]); + } + } + + r->current = 0; + + return r; +} + +// centisecond: 1/100 second +static inline void +systime(uint32_t *sec, uint32_t *cs) +{ +#if !defined(__APPLE__) || defined(AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER) + struct timespec ti; + clock_gettime(CLOCK_REALTIME, &ti); + *sec = (uint32_t)ti.tv_sec; + *cs = (uint32_t)(ti.tv_nsec / 10000000); +#else + struct timeval tv; + gettimeofday(&tv, NULL); + *sec = tv.tv_sec; + *cs = tv.tv_usec / 10000; +#endif +} + +static inline uint64_t +gettime() +{ + uint64_t t; +#if !defined(__APPLE__) || defined(AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER) + struct timespec ti; + clock_gettime(CLOCK_MONOTONIC, &ti); + t = (uint64_t)ti.tv_sec * 100; + t += ti.tv_nsec / 10000000; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + t = (uint64_t)tv.tv_sec * 100; + t += tv.tv_usec / 10000; +#endif + return t; +} + +// for profile + +#define NANOSEC 1000000000 +#define MICROSEC 1000000 + +uint64_t +skynet_thread_time(void) +{ +#if !defined(__APPLE__) || defined(AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER) + struct timespec ti; + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ti); + + return (uint64_t)ti.tv_sec * MICROSEC + (uint64_t)ti.tv_nsec / (NANOSEC / MICROSEC); +#else + struct task_thread_times_info aTaskInfo; + mach_msg_type_number_t aTaskInfoCount = TASK_THREAD_TIMES_INFO_COUNT; + if (KERN_SUCCESS != task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t)&aTaskInfo, &aTaskInfoCount)) + { + return 0; + } + + return (uint64_t)(aTaskInfo.user_time.seconds) + (uint64_t)aTaskInfo.user_time.microseconds; +#endif +} + +static inline struct timer * +_to_timer(lua_State *L) +{ + struct timer **t = lua_touserdata(L, 1); + if (t == NULL) + { + luaL_error(L, "must be timer object"); + } + return *t; +} + +#define INVALID_VALUE -1 +static inline int +_add(lua_State *L) +{ + struct timer *pt = _to_timer(L); + int second = luaL_checkinteger(L, 2); + + pt->cnt++; + // 添加计时器 + struct timer_event event; + // 初始赋值 + event.near = INVALID_VALUE; + event.level = INVALID_VALUE; + event.rear = INVALID_VALUE; + + struct timer_node *ptnode = timer_add(pt, &event, sizeof(event), second); + ptnode->id = pt->cnt; //记录ID + + lua_pushinteger(L, pt->cnt); + lua_pushlightuserdata(L, ptnode); + return 2; +} + +static inline int +_del(lua_State *L) +{ + struct timer *pt = _to_timer(L); + struct timer_node *ptnode = lua_touserdata(L, 2); + struct timer_event *pevent = (struct timer_event *)(ptnode + 1); + struct link_list *list = 0; + if (pevent->near != INVALID_VALUE) + { + list = &pt->near[pevent->near]; + } + else if (pevent->rear != INVALID_VALUE && pevent->level != INVALID_VALUE) + { + list = &pt->t[pevent->rear][pevent->level]; + } + short found = 0; + // 头节点 + struct timer_node *current = link_clear(list); + while (current) + { + if (current == ptnode) + { //去掉当前节点,并跳过当前节点,直接遍历下个节点 + found = 1; + struct timer_node *temp = current->next; + struct timer_node *removenode = current; + skynet_free(removenode); + current = temp; + } + else + { + struct timer_node *temp = current->next; + add_node(pt, current); + current = temp; + } + } + lua_pushboolean(L, found); + return 1; +} + +static int +_new(lua_State *L) +{ + struct timer *pt = timer_create_timer(); + uint32_t current = 0; + systime(&pt->starttime, ¤t); + pt->current = current; + pt->current_point = gettime(); + pt->cnt = 0; + + struct timer **t = (struct timer **)lua_newuserdata(L, sizeof(struct timer *)); + *t = pt; + lua_pushvalue(L, lua_upvalueindex(1)); + lua_setmetatable(L, -2); + return 1; +} + +static inline int +_release(lua_State *L) +{ + struct timer *pt = _to_timer(L); + // printf("collect pt:%p\n", pt); + skynet_free(pt); + return 0; +} + +static inline int +_inc(lua_State *L) +{ + struct timer *pt = _to_timer(L); + pt->cnt++; + lua_pushinteger(L, pt->cnt); + return 1; +} + +static inline int +_update(lua_State *L) +{ + struct timer *pt = _to_timer(L); + + // 返回数据 + int tidx = 0; + uint64_t cp = gettime(); + if (cp < pt->current_point) + { + // printf("time diff error: change from %lld to %lld", cp, pt->current_point); + pt->current_point = cp; + } + else if (cp != pt->current_point) + { + uint32_t diff = (uint32_t)(cp - pt->current_point); + pt->current_point = cp; + pt->current += diff; + // printf("current=%d,cpoint=%d,diff=%d,cp=%d\n",pt->current,pt->current_point,diff,cp); + int i; + short hasdata = 0; + for (i = 0; i < diff; i++) + { + timer_execute(L, pt, &hasdata, &tidx); + timer_shift(pt); + timer_execute(L, pt, &hasdata, &tidx); + } + } + if (tidx == 0) + { + lua_pushnil(L); + } + return 1; +} + +int luaopen_shiftimer_c(lua_State *L) +{ + luaL_checkversion(L); + + luaL_Reg l[] = { + {"add", _add}, + {"del", _del}, + {"nextid", _inc}, + {"update", _update}, + {NULL, NULL}}; + + lua_createtable(L, 0, 2); + + luaL_newlib(L, l); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, _release); + lua_setfield(L, -2, "__gc"); + + lua_pushcclosure(L, _new, 1); + return 1; +} diff --git a/framework/lualib-src/lua-timer/lua-timer.h b/framework/lualib-src/lua-timer/lua-timer.h new file mode 100755 index 0000000..d18cb04 --- /dev/null +++ b/framework/lualib-src/lua-timer/lua-timer.h @@ -0,0 +1,11 @@ +#ifndef SKYNET_TIMER_H +#define SKYNET_TIMER_H + +#include + +int skynet_timeout(uint32_t handle, int time, int session); +void skynet_updatetime(void); +uint32_t skynet_starttime(void); +uint64_t skynet_thread_time(void); // for profile, in micro second + +#endif diff --git a/framework/lualib-src/lua-utf8/Makefile b/framework/lualib-src/lua-utf8/Makefile new file mode 100644 index 0000000..9c53a40 --- /dev/null +++ b/framework/lualib-src/lua-utf8/Makefile @@ -0,0 +1,30 @@ +SKYNET_ROOT ?= ../../skynet +include $(SKYNET_ROOT)/platform.mk + +PLAT ?= none + +TARGET = ../../luaclib/utf8.so + +ifeq ($(PLAT), macosx) + CFLAGS = -g -O2 -dynamiclib -Wl,-undefined,dynamic_lookup +else +ifeq ($(PLAT), linux) + CFLAGS = -g -O2 -shared -fPIC +endif +endif + +LUA_LIB ?= $(SKYNET_ROOT)/3rd/lua/ +LUA_INC ?= $(SKYNET_ROOT)/3rd/lua/ +SKYNET_SRC ?= $(SKYNET_ROOT)/skynet-src + +SRC = . + +.PHONY: all clean + +all: $(TARGET) + +$(TARGET): $(foreach dir, $(SRC), $(wildcard $(dir)/*.c)) + $(CC) $(CFLAGS) $(SHARED) -I$(LUA_INC) -I$(SKYNET_SRC) $^ -o $@ + +clean: + rm -f *.o $(TARGET) \ No newline at end of file diff --git a/framework/lualib-src/lua-utf8/lua-utf8.c b/framework/lualib-src/lua-utf8/lua-utf8.c new file mode 100644 index 0000000..c3f5cf8 --- /dev/null +++ b/framework/lualib-src/lua-utf8/lua-utf8.c @@ -0,0 +1,169 @@ +#include +#include +#include + +#include "lua.h" +#include "lauxlib.h" + +inline static int +_steps(uint8_t c) { + if(c < 0x80) return 1; + if(c < 0xc0) return 0; + if(c < 0xe0) return 2; + if(c < 0xf0) return 3; + if(c < 0xf8) return 4; + return 0; +} + +inline static int +_bytes(uint32_t rune) { + if(rune < 0x80) return 1; + if(rune < 0x800) return 2; + if(rune < 0x10000) return 3; + if(rune < 0x110000) return 4; + return 0; +} + +inline static uint32_t +_decode(const char *str, int i, int step) { + uint8_t c = str[i]; + uint32_t v = c & (0xff >> step); + int j = 1; + for(;j>= 6; + +inline static uint8_t* +_encode(uint32_t rune, int bytes, uint8_t* str) { + if (bytes == 1) { + str[0] = rune & 0x7f; + } else if(bytes == 2) { + FILL_LOW_BITS(str, 1, rune) + str[0] = rune | 0xc0; + } else if(bytes == 3) { + FILL_LOW_BITS(str, 2, rune) + FILL_LOW_BITS(str, 1, rune) + str[0] = rune | 0xe0; + } else { + FILL_LOW_BITS(str, 3, rune) + FILL_LOW_BITS(str, 2, rune) + FILL_LOW_BITS(str, 1, rune) + str[0] = rune | 0xf0; + } + return str + bytes; +} + +static int +_toutf32(lua_State *L) { + size_t len; + const char* str = luaL_checklstring(L, 1, &len); + luaL_checktype(L, 2, LUA_TTABLE); + + int count = 0; + + int i, step; + uint8_t c; + for(i=0;i +#include + +#include "lua.h" +#include "lauxlib.h" +#include "skiplist.h" + +static inline skiplist* +_to_skiplist(lua_State *L) { + skiplist **sl = lua_touserdata(L, 1); + if(sl==NULL) { + luaL_error(L, "must be skiplist object"); + } + return *sl; +} + +static int +_insert(lua_State *L) { + skiplist *sl = _to_skiplist(L); + double score = luaL_checknumber(L, 2); + luaL_checktype(L, 3, LUA_TSTRING); + size_t len; + const char* ptr = lua_tolstring(L, 3, &len); + slobj *obj = slCreateObj(ptr, len); + slInsert(sl, score, obj); + return 0; +} + +static int +_delete(lua_State *L) { + skiplist *sl = _to_skiplist(L); + double score = luaL_checknumber(L, 2); + luaL_checktype(L, 3, LUA_TSTRING); + slobj obj; + obj.ptr = (char *)lua_tolstring(L, 3, &obj.length); + lua_pushboolean(L, slDelete(sl, score, &obj)); + return 1; +} + +static void +_delete_rank_cb(void* ud, slobj *obj) { + lua_State *L = (lua_State*)ud; + lua_pushvalue(L, 4); + lua_pushlstring(L, obj->ptr, obj->length); + lua_call(L, 1, 0); +} + +static int +_delete_by_rank(lua_State *L) { + skiplist *sl = _to_skiplist(L); + unsigned int start = luaL_checkinteger(L, 2); + unsigned int end = luaL_checkinteger(L, 3); + luaL_checktype(L, 4, LUA_TFUNCTION); + if (start > end) { + unsigned int tmp = start; + start = end; + end = tmp; + } + + lua_pushinteger(L, slDeleteByRank(sl, start, end, _delete_rank_cb, L)); + return 1; +} + +static int +_get_count(lua_State *L) { + skiplist *sl = _to_skiplist(L); + lua_pushinteger(L, sl->length); + return 1; +} + +static int +_get_rank(lua_State *L) { + skiplist *sl = _to_skiplist(L); + double score = luaL_checknumber(L, 2); + luaL_checktype(L, 3, LUA_TSTRING); + slobj obj; + obj.ptr = (char *)lua_tolstring(L, 3, &obj.length); + + unsigned long rank = slGetRank(sl, score, &obj); + if(rank == 0) { + return 0; + } + + lua_pushinteger(L, rank); + + return 1; +} + +static int +_get_rank_range(lua_State *L) { + skiplist *sl = _to_skiplist(L); + unsigned long r1 = luaL_checkinteger(L, 2); + unsigned long r2 = luaL_checkinteger(L, 3); + int reverse, rangelen; + if(r1 <= r2) { + reverse = 0; + rangelen = r2 - r1 + 1; + } else { + reverse = 1; + rangelen = r1 - r2 + 1; + } + + skiplistNode* node = slGetNodeByRank(sl, r1); + lua_createtable(L, rangelen, 0); + int n = 0; + while(node && n < rangelen) { + n++; + + lua_pushlstring(L, node->obj->ptr, node->obj->length); + lua_rawseti(L, -2, n); + node = reverse? node->backward : node->level[0].forward; + } + return 1; +} + +static int +_get_score_range(lua_State *L) { + skiplist *sl = _to_skiplist(L); + double s1 = luaL_checknumber(L, 2); + double s2 = luaL_checknumber(L, 3); + int reverse; + skiplistNode *node; + + if(s1 <= s2) { + reverse = 0; + node = slFirstInRange(sl, s1, s2); + } else { + reverse = 1; + node = slLastInRange(sl, s2, s1); + } + + lua_newtable(L); + int n = 0; + while(node) { + if(reverse) { + if(node->score < s2) break; + } else { + if(node->score > s2) break; + } + n++; + + lua_pushlstring(L, node->obj->ptr, node->obj->length); + lua_rawseti(L, -2, n); + + node = reverse? node->backward:node->level[0].forward; + } + return 1; +} + +static int +_get_member_by_rank(lua_State *L){ + skiplist *sl = _to_skiplist(L); + unsigned long r = luaL_checkinteger(L, 2); + skiplistNode *node = slGetNodeByRank(sl, r); + if (node) { + lua_pushlstring(L, node->obj->ptr, node->obj->length); + return 1; + } + return 0; +} + +static int +_dump(lua_State *L) { + skiplist *sl = _to_skiplist(L); + slDump(sl); + return 0; +} + +static int +_new(lua_State *L) { + skiplist *psl = slCreate(); + + skiplist **sl = (skiplist**) lua_newuserdata(L, sizeof(skiplist*)); + *sl = psl; + lua_pushvalue(L, lua_upvalueindex(1)); + lua_setmetatable(L, -2); + return 1; +} + +static int +_release(lua_State *L) { + skiplist *sl = _to_skiplist(L); + printf("collect sl:%p\n", sl); + slFree(sl); + return 0; +} + +int luaopen_skiplist_c(lua_State *L) { +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM > 501 + luaL_checkversion(L); +#endif + + luaL_Reg l[] = { + {"insert", _insert}, + {"delete", _delete}, + {"delete_by_rank", _delete_by_rank}, + + {"get_count", _get_count}, + {"get_rank", _get_rank}, + {"get_rank_range", _get_rank_range}, + {"get_score_range", _get_score_range}, + {"get_member_by_rank", _get_member_by_rank}, + + {"dump", _dump}, + {NULL, NULL} + }; + + lua_createtable(L, 0, 2); + + luaL_newlib(L, l); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, _release); + lua_setfield(L, -2, "__gc"); + + lua_pushcclosure(L, _new, 1); + return 1; +} diff --git a/framework/lualib-src/lua-zset/skiplist.c b/framework/lualib-src/lua-zset/skiplist.c new file mode 100644 index 0000000..2c662d0 --- /dev/null +++ b/framework/lualib-src/lua-zset/skiplist.c @@ -0,0 +1,347 @@ +/* + * author: xjdrew + * date: 2014-06-03 20:38 + */ + +// skiplist similar with the version in redis +#include +#include +#include +#include + +#include "skiplist.h" + + +skiplistNode *slCreateNode(int level, double score, slobj *obj) { + skiplistNode *n = malloc(sizeof(*n) + level * sizeof(struct skiplistLevel)); + n->score = score; + n->obj = obj; + return n; +} + +skiplist *slCreate(void) { + int j; + skiplist *sl; + + sl = malloc(sizeof(*sl)); + sl->level = 1; + sl->length = 0; + sl->header = slCreateNode(SKIPLIST_MAXLEVEL, 0, NULL); + for (j=0; j < SKIPLIST_MAXLEVEL; j++) { + sl->header->level[j].forward = NULL; + sl->header->level[j].span = 0; + } + sl->header->backward = NULL; + sl->tail = NULL; + return sl; +} + +slobj* slCreateObj(const char* ptr, size_t length) { + slobj *obj = malloc(sizeof(*obj)); + obj->ptr = malloc(length + 1); + + if(ptr) { + memcpy(obj->ptr, ptr, length); + } + obj->ptr[length] = '\0'; + + obj->length = length; + return obj; +} + +void slFreeObj(slobj *obj) { + free(obj->ptr); + free(obj); +} + +void slFreeNode(skiplistNode *node) { + slFreeObj(node->obj); + free(node); +} + +void slFree(skiplist *sl) { + skiplistNode *node = sl->header->level[0].forward, *next; + + free(sl->header); + while(node) { + next = node->level[0].forward; + slFreeNode(node); + node = next; + } + free(sl); +} + +int slRandomLevel(void) { + int level = 1; + while((random() & 0xffff) < (SKIPLIST_P * 0xffff)) + level += 1; + return (level < SKIPLIST_MAXLEVEL) ? level : SKIPLIST_MAXLEVEL; +} + +int compareslObj(slobj *a, slobj *b) { + int cmp = memcmp(a->ptr, b->ptr, a->length <= b->length ? a->length : b->length); + if(cmp == 0) return a->length - b->length; + return cmp; +} + +int equalslObj(slobj *a, slobj *b) { + return compareslObj(a, b) == 0; +} + +void slInsert(skiplist *sl, double score, slobj *obj) { + skiplistNode *update[SKIPLIST_MAXLEVEL], *x; + unsigned int rank[SKIPLIST_MAXLEVEL]; + int i, level; + + x = sl->header; + for (i = sl->level-1; i >= 0; i--) { + /* store rank that is crossed to reach the insert position */ + rank[i] = i == (sl->level-1) ? 0 : rank[i+1]; + while (x->level[i].forward && + (x->level[i].forward->score < score || + (x->level[i].forward->score == score && + compareslObj(x->level[i].forward->obj,obj) < 0))) { + rank[i] += x->level[i].span; + x = x->level[i].forward; + } + update[i] = x; + } + /* we assume the key is not already inside, since we allow duplicated + * scores, and the re-insertion of score and redis object should never + * happen since the caller of slInsert() should test in the hash table + * if the element is already inside or not. */ + level = slRandomLevel(); + if (level > sl->level) { + for (i = sl->level; i < level; i++) { + rank[i] = 0; + update[i] = sl->header; + update[i]->level[i].span = sl->length; + } + sl->level = level; + } + x = slCreateNode(level,score,obj); + for (i = 0; i < level; i++) { + x->level[i].forward = update[i]->level[i].forward; + update[i]->level[i].forward = x; + + /* update span covered by update[i] as x is inserted here */ + x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]); + update[i]->level[i].span = (rank[0] - rank[i]) + 1; + } + + /* increment span for untouched levels */ + for (i = level; i < sl->level; i++) { + update[i]->level[i].span++; + } + + x->backward = (update[0] == sl->header) ? NULL : update[0]; + if (x->level[0].forward) + x->level[0].forward->backward = x; + else + sl->tail = x; + sl->length++; +} + +/* Internal function used by slDelete, slDeleteByScore */ +void slDeleteNode(skiplist *sl, skiplistNode *x, skiplistNode **update) { + int i; + for (i = 0; i < sl->level; i++) { + if (update[i]->level[i].forward == x) { + update[i]->level[i].span += x->level[i].span - 1; + update[i]->level[i].forward = x->level[i].forward; + } else { + update[i]->level[i].span -= 1; + } + } + if (x->level[0].forward) { + x->level[0].forward->backward = x->backward; + } else { + sl->tail = x->backward; + } + while(sl->level > 1 && sl->header->level[sl->level-1].forward == NULL) + sl->level--; + sl->length--; +} + +/* Delete an element with matching score/object from the skiplist. */ +int slDelete(skiplist *sl, double score, slobj *obj) { + skiplistNode *update[SKIPLIST_MAXLEVEL], *x; + int i; + + x = sl->header; + for (i = sl->level-1; i >= 0; i--) { + while (x->level[i].forward && + (x->level[i].forward->score < score || + (x->level[i].forward->score == score && + compareslObj(x->level[i].forward->obj,obj) < 0))) + x = x->level[i].forward; + update[i] = x; + } + /* We may have multiple elements with the same score, what we need + * is to find the element with both the right score and object. */ + x = x->level[0].forward; + if (x && score == x->score && equalslObj(x->obj,obj)) { + slDeleteNode(sl, x, update); + slFreeNode(x); + return 1; + } else { + return 0; /* not found */ + } + return 0; /* not found */ +} + +/* Delete all the elements with rank between start and end from the skiplist. + * Start and end are inclusive. Note that start and end need to be 1-based */ +unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, slDeleteCb cb, void* ud) { + skiplistNode *update[SKIPLIST_MAXLEVEL], *x; + unsigned long traversed = 0, removed = 0; + int i; + + x = sl->header; + for (i = sl->level-1; i >= 0; i--) { + while (x->level[i].forward && (traversed + x->level[i].span) < start) { + traversed += x->level[i].span; + x = x->level[i].forward; + } + update[i] = x; + } + + traversed++; + x = x->level[0].forward; + while (x && traversed <= end) { + skiplistNode *next = x->level[0].forward; + slDeleteNode(sl,x,update); + cb(ud, x->obj); + slFreeNode(x); + removed++; + traversed++; + x = next; + } + return removed; +} + +/* Find the rank for an element by both score and key. + * Returns 0 when the element cannot be found, rank otherwise. + * Note that the rank is 1-based due to the span of sl->header to the + * first element. */ +unsigned long slGetRank(skiplist *sl, double score, slobj *o) { + skiplistNode *x; + unsigned long rank = 0; + int i; + + x = sl->header; + for (i = sl->level-1; i >= 0; i--) { + while (x->level[i].forward && + (x->level[i].forward->score < score || + (x->level[i].forward->score == score && + compareslObj(x->level[i].forward->obj,o) <= 0))) { + rank += x->level[i].span; + x = x->level[i].forward; + } + + /* x might be equal to sl->header, so test if obj is non-NULL */ + if (x->obj && equalslObj(x->obj, o)) { + return rank; + } + } + return 0; +} + +/* Finds an element by its rank. The rank argument needs to be 1-based. */ +skiplistNode* slGetNodeByRank(skiplist *sl, unsigned long rank) { + if(rank == 0 || rank > sl->length) { + return NULL; + } + + skiplistNode *x; + unsigned long traversed = 0; + int i; + + x = sl->header; + for (i = sl->level-1; i >= 0; i--) { + while (x->level[i].forward && (traversed + x->level[i].span) <= rank) + { + traversed += x->level[i].span; + x = x->level[i].forward; + } + if (traversed == rank) { + return x; + } + } + + return NULL; +} + +/* range [min, max], left & right both include */ +/* Returns if there is a part of the zset is in range. */ +int slIsInRange(skiplist *sl, double min, double max) { + skiplistNode *x; + + /* Test for ranges that will always be empty. */ + if(min > max) { + return 0; + } + x = sl->tail; + if (x == NULL || x->score < min) + return 0; + + x = sl->header->level[0].forward; + if (x == NULL || x->score > max) + return 0; + return 1; +} + +/* Find the first node that is contained in the specified range. + * Returns NULL when no element is contained in the range. */ +skiplistNode *slFirstInRange(skiplist *sl, double min, double max) { + skiplistNode *x; + int i; + + /* If everything is out of range, return early. */ + if (!slIsInRange(sl,min, max)) return NULL; + + x = sl->header; + for (i = sl->level-1; i >= 0; i--) { + /* Go forward while *OUT* of range. */ + while (x->level[i].forward && x->level[i].forward->score < min) + x = x->level[i].forward; + } + + /* This is an inner range, so the next node cannot be NULL. */ + x = x->level[0].forward; + return x; +} + +/* Find the last node that is contained in the specified range. + * Returns NULL when no element is contained in the range. */ +skiplistNode *slLastInRange(skiplist *sl, double min, double max) { + skiplistNode *x; + int i; + + /* If everything is out of range, return early. */ + if (!slIsInRange(sl, min, max)) return NULL; + + x = sl->header; + for (i = sl->level-1; i >= 0; i--) { + /* Go forward while *IN* range. */ + while (x->level[i].forward && + x->level[i].forward->score <= max) + x = x->level[i].forward; + } + + /* This is an inner range, so this node cannot be NULL. */ + return x; +} + +void slDump(skiplist *sl) { + skiplistNode *x; + int i; + + x = sl->header; + i = 0; + while(x->level[0].forward) { + x = x->level[0].forward; + i++; + printf("node %d: score:%f, member:%s\n", i, x->score, x->obj->ptr); + } +} diff --git a/framework/lualib-src/lua-zset/skiplist.h b/framework/lualib-src/lua-zset/skiplist.h new file mode 100644 index 0000000..5a7f8f8 --- /dev/null +++ b/framework/lualib-src/lua-zset/skiplist.h @@ -0,0 +1,44 @@ +// +#include + +#define SKIPLIST_MAXLEVEL 32 +#define SKIPLIST_P 0.25 + +typedef struct slobj { + char *ptr; + size_t length; +} slobj; + +typedef struct skiplistNode { + slobj* obj; + double score; + struct skiplistNode *backward; + struct skiplistLevel { + struct skiplistNode *forward; + unsigned int span; + }level[]; +} skiplistNode; + +typedef struct skiplist { + struct skiplistNode *header, *tail; + unsigned long length; + int level; +} skiplist; + +typedef void (*slDeleteCb) (void *ud, slobj *obj); +slobj* slCreateObj(const char* ptr, size_t length); +void slFreeObj(slobj *obj); + +skiplist *slCreate(void); +void slFree(skiplist *sl); +void slDump(skiplist *sl); + +void slInsert(skiplist *sl, double score, slobj *obj); +int slDelete(skiplist *sl, double score, slobj *obj); +unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, slDeleteCb cb, void* ud); + +unsigned long slGetRank(skiplist *sl, double score, slobj *o); +skiplistNode* slGetNodeByRank(skiplist *sl, unsigned long rank); + +skiplistNode *slFirstInRange(skiplist *sl, double min, double max); +skiplistNode *slLastInRange(skiplist *sl, double min, double max); diff --git a/framework/lualib-src/testluaclib.lua b/framework/lualib-src/testluaclib.lua new file mode 100644 index 0000000..dbce07e --- /dev/null +++ b/framework/lualib-src/testluaclib.lua @@ -0,0 +1,35 @@ +package.path = "3rd/?.lua;" .. package.path +package.cpath = ";luaclib/?.so;skynet/luaclib/?.so;" + +local geohash = require "geohash" +print("geohash", geohash) + +local snapshot = require "snapshot" +print("snapshot", snapshot) + +local cjson = require "cjson" +print("cjson", cjson) + +local lfs = require "lfs" +print("lfs", lfs) + +local crab = require "crab.c" +print("crab", crab) + +local utf8 = require "utf8.c" +print("utf8", utf8) + +local shiftimer = require "shiftimer.c" +print("shiftimer", shiftimer) + +local skiplist = require "skiplist.c" +print("skiplist", skiplist) + +local profile = require "profile.c" +print("profile", profile) + +local clua = require "clua" +print("clua", clua) + +local fmt = require "lfmt" +print("fmt", fmt) \ No newline at end of file diff --git a/framework/lualib/thirdparty/ luafun/fun.lua b/framework/lualib/thirdparty/ luafun/fun.lua new file mode 100644 index 0000000..214bd9c --- /dev/null +++ b/framework/lualib/thirdparty/ luafun/fun.lua @@ -0,0 +1,1124 @@ +--- +--- Lua Fun - a high-performance functional programming library for LuaJIT +--- +--- Copyright (c) 2013-2017 Roman Tsisyk +--- +--- Distributed under the MIT/X11 License. See COPYING.md for more details. +--- +local exports = {} +local methods = {} + +-- compatibility with Lua 5.1/5.2 +local unpack = rawget(table, "unpack") or unpack + +-------------------------------------------------------------------------------- +-- Tools +-------------------------------------------------------------------------------- + +local return_if_not_empty = function(state_x, ...) + if state_x == nil then + return nil + end + return ... +end + +local call_if_not_empty = function(fun, state_x, ...) + if state_x == nil then + return nil + end + return state_x, fun(...) +end + +local function deepcopy(orig) -- used by cycle() + local orig_type = type(orig) + local copy + if orig_type == 'table' then + copy = {} + for orig_key, orig_value in next, orig, nil do + copy[deepcopy(orig_key)] = deepcopy(orig_value) + end + else + copy = orig + end + return copy +end + +local iterator_mt = { + -- usually called by for-in loop + __call = function(self, param, state) + return self.gen(param, state) + end, + __tostring = function(self) + return '' + end, + -- add all exported methods + __index = methods, +} + +local wrap = function(gen, param, state) + return setmetatable({ + gen = gen, + param = param, + state = state, + }, iterator_mt), param, state +end +exports.wrap = wrap + +local unwrap = function(self) + return self.gen, self.param, self.state +end +methods.unwrap = unwrap + +-------------------------------------------------------------------------------- +-- Basic Functions +-------------------------------------------------------------------------------- + +local nil_gen = function(_param, _state) + return nil +end + +local string_gen = function(param, state) + local state = state + 1 + if state > #param then + return nil + end + local r = string.sub(param, state, state) + return state, r +end + +local ipairs_gen = ipairs({}) -- get the generating function from ipairs + +local pairs_gen = pairs({ + a = 0, +}) -- get the generating function from pairs +local map_gen = function(tab, key) + local value + local key, value = pairs_gen(tab, key) + return key, key, value +end + +local rawiter = function(obj, param, state) + assert(obj ~= nil, "invalid iterator") + if type(obj) == "table" then + local mt = getmetatable(obj); + if mt ~= nil then + if mt == iterator_mt then + return obj.gen, obj.param, obj.state + elseif mt.__ipairs ~= nil then + return mt.__ipairs(obj) + elseif mt.__pairs ~= nil then + return mt.__pairs(obj) + end + end + if #obj > 0 then + -- array + return ipairs(obj) + else + -- hash + return map_gen, obj, nil + end + elseif (type(obj) == "function") then + return obj, param, state + elseif (type(obj) == "string") then + if #obj == 0 then + return nil_gen, nil, nil + end + return string_gen, obj, 0 + end + error(string.format('object %s of type "%s" is not iterable', obj, type(obj))) +end + +local iter = function(obj, param, state) + return wrap(rawiter(obj, param, state)) +end +exports.iter = iter + +local method0 = function(fun) + return function(self) + return fun(self.gen, self.param, self.state) + end +end + +local method1 = function(fun) + return function(self, arg1) + return fun(arg1, self.gen, self.param, self.state) + end +end + +local method2 = function(fun) + return function(self, arg1, arg2) + return fun(arg1, arg2, self.gen, self.param, self.state) + end +end + +local export0 = function(fun) + return function(gen, param, state) + return fun(rawiter(gen, param, state)) + end +end + +local export1 = function(fun) + return function(arg1, gen, param, state) + return fun(arg1, rawiter(gen, param, state)) + end +end + +local export2 = function(fun) + return function(arg1, arg2, gen, param, state) + return fun(arg1, arg2, rawiter(gen, param, state)) + end +end + +local each = function(fun, gen, param, state) + repeat + state = call_if_not_empty(fun, gen(param, state)) + until state == nil +end +methods.each = method1(each) +exports.each = export1(each) +methods.for_each = methods.each +exports.for_each = exports.each +methods.foreach = methods.each +exports.foreach = exports.each + +-------------------------------------------------------------------------------- +-- Generators +-------------------------------------------------------------------------------- + +local range_gen = function(param, state) + local stop, step = param[1], param[2] + local state = state + step + if state > stop then + return nil + end + return state, state +end + +local range_rev_gen = function(param, state) + local stop, step = param[1], param[2] + local state = state + step + if state < stop then + return nil + end + return state, state +end + +local range = function(start, stop, step) + if step == nil then + if stop == nil then + if start == 0 then + return nil_gen, nil, nil + end + stop = start + start = stop > 0 and 1 or -1 + end + step = start <= stop and 1 or -1 + end + + assert(type(start) == "number", "start must be a number") + assert(type(stop) == "number", "stop must be a number") + assert(type(step) == "number", "step must be a number") + assert(step ~= 0, "step must not be zero") + + if (step > 0) then + return wrap(range_gen, {stop, step}, start - step) + elseif (step < 0) then + return wrap(range_rev_gen, {stop, step}, start - step) + end +end +exports.range = range + +local duplicate_table_gen = function(param_x, state_x) + return state_x + 1, unpack(param_x) +end + +local duplicate_fun_gen = function(param_x, state_x) + return state_x + 1, param_x(state_x) +end + +local duplicate_gen = function(param_x, state_x) + return state_x + 1, param_x +end + +local duplicate = function(...) + if select('#', ...) <= 1 then + return wrap(duplicate_gen, select(1, ...), 0) + else + return wrap(duplicate_table_gen, {...}, 0) + end +end +exports.duplicate = duplicate +exports.replicate = duplicate +exports.xrepeat = duplicate + +local tabulate = function(fun) + assert(type(fun) == "function") + return wrap(duplicate_fun_gen, fun, 0) +end +exports.tabulate = tabulate + +local zeros = function() + return wrap(duplicate_gen, 0, 0) +end +exports.zeros = zeros + +local ones = function() + return wrap(duplicate_gen, 1, 0) +end +exports.ones = ones + +local rands_gen = function(param_x, _state_x) + return 0, math.random(param_x[1], param_x[2]) +end + +local rands_nil_gen = function(_param_x, _state_x) + return 0, math.random() +end + +local rands = function(n, m) + if n == nil and m == nil then + return wrap(rands_nil_gen, 0, 0) + end + assert(type(n) == "number", "invalid first arg to rands") + if m == nil then + m = n + n = 0 + else + assert(type(m) == "number", "invalid second arg to rands") + end + assert(n < m, "empty interval") + return wrap(rands_gen, {n, m - 1}, 0) +end +exports.rands = rands + +-------------------------------------------------------------------------------- +-- Slicing +-------------------------------------------------------------------------------- + +local nth = function(n, gen_x, param_x, state_x) + assert(n > 0, "invalid first argument to nth") + -- An optimization for arrays and strings + if gen_x == ipairs_gen then + return param_x[n] + elseif gen_x == string_gen then + if n <= #param_x then + return string.sub(param_x, n, n) + else + return nil + end + end + for i = 1, n - 1, 1 do + state_x = gen_x(param_x, state_x) + if state_x == nil then + return nil + end + end + return return_if_not_empty(gen_x(param_x, state_x)) +end +methods.nth = method1(nth) +exports.nth = export1(nth) + +local head_call = function(state, ...) + if state == nil then + error("head: iterator is empty") + end + return ... +end + +local head = function(gen, param, state) + return head_call(gen(param, state)) +end +methods.head = method0(head) +exports.head = export0(head) +exports.car = exports.head +methods.car = methods.head + +local tail = function(gen, param, state) + state = gen(param, state) + if state == nil then + return wrap(nil_gen, nil, nil) + end + return wrap(gen, param, state) +end +methods.tail = method0(tail) +exports.tail = export0(tail) +exports.cdr = exports.tail +methods.cdr = methods.tail + +local take_n_gen_x = function(i, state_x, ...) + if state_x == nil then + return nil + end + return {i, state_x}, ... +end + +local take_n_gen = function(param, state) + local n, gen_x, param_x = param[1], param[2], param[3] + local i, state_x = state[1], state[2] + if i >= n then + return nil + end + return take_n_gen_x(i + 1, gen_x(param_x, state_x)) +end + +local take_n = function(n, gen, param, state) + assert(n >= 0, "invalid first argument to take_n") + return wrap(take_n_gen, {n, gen, param}, {0, state}) +end +methods.take_n = method1(take_n) +exports.take_n = export1(take_n) + +local take_while_gen_x = function(fun, state_x, ...) + if state_x == nil or not fun(...) then + return nil + end + return state_x, ... +end + +local take_while_gen = function(param, state_x) + local fun, gen_x, param_x = param[1], param[2], param[3] + return take_while_gen_x(fun, gen_x(param_x, state_x)) +end + +local take_while = function(fun, gen, param, state) + assert(type(fun) == "function", "invalid first argument to take_while") + return wrap(take_while_gen, {fun, gen, param}, state) +end +methods.take_while = method1(take_while) +exports.take_while = export1(take_while) + +local take = function(n_or_fun, gen, param, state) + if type(n_or_fun) == "number" then + return take_n(n_or_fun, gen, param, state) + else + return take_while(n_or_fun, gen, param, state) + end +end +methods.take = method1(take) +exports.take = export1(take) + +local drop_n = function(n, gen, param, state) + assert(n >= 0, "invalid first argument to drop_n") + local i + for i = 1, n, 1 do + state = gen(param, state) + if state == nil then + return wrap(nil_gen, nil, nil) + end + end + return wrap(gen, param, state) +end +methods.drop_n = method1(drop_n) +exports.drop_n = export1(drop_n) + +local drop_while_x = function(fun, state_x, ...) + if state_x == nil or not fun(...) then + return state_x, false + end + return state_x, true, ... +end + +local drop_while = function(fun, gen_x, param_x, state_x) + assert(type(fun) == "function", "invalid first argument to drop_while") + local cont, state_x_prev + repeat + state_x_prev = deepcopy(state_x) + state_x, cont = drop_while_x(fun, gen_x(param_x, state_x)) + until not cont + if state_x == nil then + return wrap(nil_gen, nil, nil) + end + return wrap(gen_x, param_x, state_x_prev) +end +methods.drop_while = method1(drop_while) +exports.drop_while = export1(drop_while) + +local drop = function(n_or_fun, gen_x, param_x, state_x) + if type(n_or_fun) == "number" then + return drop_n(n_or_fun, gen_x, param_x, state_x) + else + return drop_while(n_or_fun, gen_x, param_x, state_x) + end +end +methods.drop = method1(drop) +exports.drop = export1(drop) + +local split = function(n_or_fun, gen_x, param_x, state_x) + return take(n_or_fun, gen_x, param_x, state_x), drop(n_or_fun, gen_x, param_x, state_x) +end +methods.split = method1(split) +exports.split = export1(split) +methods.split_at = methods.split +exports.split_at = exports.split +methods.span = methods.split +exports.span = exports.split + +-------------------------------------------------------------------------------- +-- Indexing +-------------------------------------------------------------------------------- + +local index = function(x, gen, param, state) + local i = 1 + for _k, r in gen, param, state do + if r == x then + return i + end + i = i + 1 + end + return nil +end +methods.index = method1(index) +exports.index = export1(index) +methods.index_of = methods.index +exports.index_of = exports.index +methods.elem_index = methods.index +exports.elem_index = exports.index + +local indexes_gen = function(param, state) + local x, gen_x, param_x = param[1], param[2], param[3] + local i, state_x = state[1], state[2] + local r + while true do + state_x, r = gen_x(param_x, state_x) + if state_x == nil then + return nil + end + i = i + 1 + if r == x then + return {i, state_x}, i + end + end +end + +local indexes = function(x, gen, param, state) + return wrap(indexes_gen, {x, gen, param}, {0, state}) +end +methods.indexes = method1(indexes) +exports.indexes = export1(indexes) +methods.elem_indexes = methods.indexes +exports.elem_indexes = exports.indexes +methods.indices = methods.indexes +exports.indices = exports.indexes +methods.elem_indices = methods.indexes +exports.elem_indices = exports.indexes + +-------------------------------------------------------------------------------- +-- Filtering +-------------------------------------------------------------------------------- + +local filter1_gen = function(fun, gen_x, param_x, state_x, a) + while true do + if state_x == nil or fun(a) then + break + end + state_x, a = gen_x(param_x, state_x) + end + return state_x, a +end + +-- call each other +local filterm_gen +local filterm_gen_shrink = function(fun, gen_x, param_x, state_x) + return filterm_gen(fun, gen_x, param_x, gen_x(param_x, state_x)) +end + +filterm_gen = function(fun, gen_x, param_x, state_x, ...) + if state_x == nil then + return nil + end + if fun(...) then + return state_x, ... + end + return filterm_gen_shrink(fun, gen_x, param_x, state_x) +end + +local filter_detect = function(fun, gen_x, param_x, state_x, ...) + if select('#', ...) < 2 then + return filter1_gen(fun, gen_x, param_x, state_x, ...) + else + return filterm_gen(fun, gen_x, param_x, state_x, ...) + end +end + +local filter_gen = function(param, state_x) + local fun, gen_x, param_x = param[1], param[2], param[3] + return filter_detect(fun, gen_x, param_x, gen_x(param_x, state_x)) +end + +local filter = function(fun, gen, param, state) + return wrap(filter_gen, {fun, gen, param}, state) +end +methods.filter = method1(filter) +exports.filter = export1(filter) +methods.remove_if = methods.filter +exports.remove_if = exports.filter + +local grep = function(fun_or_regexp, gen, param, state) + local fun = fun_or_regexp + if type(fun_or_regexp) == "string" then + fun = function(x) + return string.find(x, fun_or_regexp) ~= nil + end + end + return filter(fun, gen, param, state) +end +methods.grep = method1(grep) +exports.grep = export1(grep) + +local partition = function(fun, gen, param, state) + local neg_fun = function(...) + return not fun(...) + end + return filter(fun, gen, param, state), filter(neg_fun, gen, param, state) +end +methods.partition = method1(partition) +exports.partition = export1(partition) + +-------------------------------------------------------------------------------- +-- Reducing +-------------------------------------------------------------------------------- + +local foldl_call = function(fun, start, state, ...) + if state == nil then + return nil, start + end + return state, fun(start, ...) +end + +local foldl = function(fun, start, gen_x, param_x, state_x) + while true do + state_x, start = foldl_call(fun, start, gen_x(param_x, state_x)) + if state_x == nil then + break + end + end + return start +end +methods.foldl = method2(foldl) +exports.foldl = export2(foldl) +methods.reduce = methods.foldl +exports.reduce = exports.foldl + +local length = function(gen, param, state) + if gen == ipairs_gen or gen == string_gen then + return #param + end + local len = 0 + repeat + state = gen(param, state) + len = len + 1 + until state == nil + return len - 1 +end +methods.length = method0(length) +exports.length = export0(length) + +local is_null = function(gen, param, state) + return gen(param, deepcopy(state)) == nil +end +methods.is_null = method0(is_null) +exports.is_null = export0(is_null) + +local is_prefix_of = function(iter_x, iter_y) + local gen_x, param_x, state_x = iter(iter_x) + local gen_y, param_y, state_y = iter(iter_y) + + local r_x, r_y + for i = 1, 10, 1 do + state_x, r_x = gen_x(param_x, state_x) + state_y, r_y = gen_y(param_y, state_y) + if state_x == nil then + return true + end + if state_y == nil or r_x ~= r_y then + return false + end + end +end +methods.is_prefix_of = is_prefix_of +exports.is_prefix_of = is_prefix_of + +local all = function(fun, gen_x, param_x, state_x) + local r + repeat + state_x, r = call_if_not_empty(fun, gen_x(param_x, state_x)) + until state_x == nil or not r + return state_x == nil +end +methods.all = method1(all) +exports.all = export1(all) +methods.every = methods.all +exports.every = exports.all + +local any = function(fun, gen_x, param_x, state_x) + local r + repeat + state_x, r = call_if_not_empty(fun, gen_x(param_x, state_x)) + until state_x == nil or r + return not not r +end +methods.any = method1(any) +exports.any = export1(any) +methods.some = methods.any +exports.some = exports.any + +local sum = function(gen, param, state) + local s = 0 + local r = 0 + repeat + s = s + r + state, r = gen(param, state) + until state == nil + return s +end +methods.sum = method0(sum) +exports.sum = export0(sum) + +local product = function(gen, param, state) + local p = 1 + local r = 1 + repeat + p = p * r + state, r = gen(param, state) + until state == nil + return p +end +methods.product = method0(product) +exports.product = export0(product) + +local min_cmp = function(m, n) + if n < m then + return n + else + return m + end +end + +local max_cmp = function(m, n) + if n > m then + return n + else + return m + end +end + +local min = function(gen, param, state) + local state, m = gen(param, state) + if state == nil then + error("min: iterator is empty") + end + + local cmp + if type(m) == "number" then + -- An optimization: use math.min for numbers + cmp = math.min + else + cmp = min_cmp + end + + for _, r in gen, param, state do + m = cmp(m, r) + end + return m +end +methods.min = method0(min) +exports.min = export0(min) +methods.minimum = methods.min +exports.minimum = exports.min + +local min_by = function(cmp, gen_x, param_x, state_x) + local state_x, m = gen_x(param_x, state_x) + if state_x == nil then + error("min: iterator is empty") + end + + for _, r in gen_x, param_x, state_x do + m = cmp(m, r) + end + return m +end +methods.min_by = method1(min_by) +exports.min_by = export1(min_by) +methods.minimum_by = methods.min_by +exports.minimum_by = exports.min_by + +local max = function(gen_x, param_x, state_x) + local state_x, m = gen_x(param_x, state_x) + if state_x == nil then + error("max: iterator is empty") + end + + local cmp + if type(m) == "number" then + -- An optimization: use math.max for numbers + cmp = math.max + else + cmp = max_cmp + end + + for _, r in gen_x, param_x, state_x do + m = cmp(m, r) + end + return m +end +methods.max = method0(max) +exports.max = export0(max) +methods.maximum = methods.max +exports.maximum = exports.max + +local max_by = function(cmp, gen_x, param_x, state_x) + local state_x, m = gen_x(param_x, state_x) + if state_x == nil then + error("max: iterator is empty") + end + + for _, r in gen_x, param_x, state_x do + m = cmp(m, r) + end + return m +end +methods.max_by = method1(max_by) +exports.max_by = export1(max_by) +methods.maximum_by = methods.maximum_by +exports.maximum_by = exports.maximum_by + +local totable = function(gen_x, param_x, state_x) + local tab, key, val = {} + while true do + state_x, val = gen_x(param_x, state_x) + if state_x == nil then + break + end + table.insert(tab, val) + end + return tab +end +methods.totable = method0(totable) +exports.totable = export0(totable) + +local tomap = function(gen_x, param_x, state_x) + local tab, key, val = {} + while true do + state_x, key, val = gen_x(param_x, state_x) + if state_x == nil then + break + end + tab[key] = val + end + return tab +end +methods.tomap = method0(tomap) +exports.tomap = export0(tomap) + +-------------------------------------------------------------------------------- +-- Transformations +-------------------------------------------------------------------------------- + +local map_gen = function(param, state) + local gen_x, param_x, fun = param[1], param[2], param[3] + return call_if_not_empty(fun, gen_x(param_x, state)) +end + +local map = function(fun, gen, param, state) + return wrap(map_gen, {gen, param, fun}, state) +end +methods.map = method1(map) +exports.map = export1(map) + +local enumerate_gen_call = function(state, i, state_x, ...) + if state_x == nil then + return nil + end + return {i + 1, state_x}, i, ... +end + +local enumerate_gen = function(param, state) + local gen_x, param_x = param[1], param[2] + local i, state_x = state[1], state[2] + return enumerate_gen_call(state, i, gen_x(param_x, state_x)) +end + +local enumerate = function(gen, param, state) + return wrap(enumerate_gen, {gen, param}, {1, state}) +end +methods.enumerate = method0(enumerate) +exports.enumerate = export0(enumerate) + +local intersperse_call = function(i, state_x, ...) + if state_x == nil then + return nil + end + return {i + 1, state_x}, ... +end + +local intersperse_gen = function(param, state) + local x, gen_x, param_x = param[1], param[2], param[3] + local i, state_x = state[1], state[2] + if i % 2 == 1 then + return {i + 1, state_x}, x + else + return intersperse_call(i, gen_x(param_x, state_x)) + end +end + +-- TODO: interperse must not add x to the tail +local intersperse = function(x, gen, param, state) + return wrap(intersperse_gen, {x, gen, param}, {0, state}) +end +methods.intersperse = method1(intersperse) +exports.intersperse = export1(intersperse) + +-------------------------------------------------------------------------------- +-- Compositions +-------------------------------------------------------------------------------- + +local function zip_gen_r(param, state, state_new, ...) + if #state_new == #param / 2 then + return state_new, ... + end + + local i = #state_new + 1 + local gen_x, param_x = param[2 * i - 1], param[2 * i] + local state_x, r = gen_x(param_x, state[i]) + if state_x == nil then + return nil + end + table.insert(state_new, state_x) + return zip_gen_r(param, state, state_new, r, ...) +end + +local zip_gen = function(param, state) + return zip_gen_r(param, state, {}) +end + +-- A special hack for zip/chain to skip last two state, if a wrapped iterator +-- has been passed +local numargs = function(...) + local n = select('#', ...) + if n >= 3 then + -- Fix last argument + local it = select(n - 2, ...) + if type(it) == 'table' and getmetatable(it) == iterator_mt and it.param == select(n - 1, ...) and it.state == + select(n, ...) then + return n - 2 + end + end + return n +end + +local zip = function(...) + local n = numargs(...) + if n == 0 then + return wrap(nil_gen, nil, nil) + end + local param = { + [2 * n] = 0, + } + local state = { + [n] = 0, + } + + local i, gen_x, param_x, state_x + for i = 1, n, 1 do + local it = select(n - i + 1, ...) + gen_x, param_x, state_x = rawiter(it) + param[2 * i - 1] = gen_x + param[2 * i] = param_x + state[i] = state_x + end + + return wrap(zip_gen, param, state) +end +methods.zip = zip +exports.zip = zip + +local cycle_gen_call = function(param, state_x, ...) + if state_x == nil then + local gen_x, param_x, state_x0 = param[1], param[2], param[3] + return gen_x(param_x, deepcopy(state_x0)) + end + return state_x, ... +end + +local cycle_gen = function(param, state_x) + local gen_x, param_x, state_x0 = param[1], param[2], param[3] + return cycle_gen_call(param, gen_x(param_x, state_x)) +end + +local cycle = function(gen, param, state) + return wrap(cycle_gen, {gen, param, state}, deepcopy(state)) +end +methods.cycle = method0(cycle) +exports.cycle = export0(cycle) + +-- call each other +local chain_gen_r1 +local chain_gen_r2 = function(param, state, state_x, ...) + if state_x == nil then + local i = state[1] + i = i + 1 + if param[3 * i - 1] == nil then + return nil + end + local state_x = param[3 * i] + return chain_gen_r1(param, {i, state_x}) + end + return {state[1], state_x}, ... +end + +chain_gen_r1 = function(param, state) + local i, state_x = state[1], state[2] + local gen_x, param_x = param[3 * i - 2], param[3 * i - 1] + return chain_gen_r2(param, state, gen_x(param_x, state[2])) +end + +local chain = function(...) + local n = numargs(...) + if n == 0 then + return wrap(nil_gen, nil, nil) + end + + local param = { + [3 * n] = 0, + } + local i, gen_x, param_x, state_x + for i = 1, n, 1 do + local elem = select(i, ...) + gen_x, param_x, state_x = iter(elem) + param[3 * i - 2] = gen_x + param[3 * i - 1] = param_x + param[3 * i] = state_x + end + + return wrap(chain_gen_r1, param, {1, param[3]}) +end +methods.chain = chain +exports.chain = chain + +-------------------------------------------------------------------------------- +-- Operators +-------------------------------------------------------------------------------- + +local operator = { + ---------------------------------------------------------------------------- + -- Comparison operators + ---------------------------------------------------------------------------- + lt = function(a, b) + return a < b + end, + le = function(a, b) + return a <= b + end, + eq = function(a, b) + return a == b + end, + ne = function(a, b) + return a ~= b + end, + ge = function(a, b) + return a >= b + end, + gt = function(a, b) + return a > b + end, + + ---------------------------------------------------------------------------- + -- Arithmetic operators + ---------------------------------------------------------------------------- + add = function(a, b) + return a + b + end, + div = function(a, b) + return a / b + end, + floordiv = function(a, b) + return math.floor(a / b) + end, + intdiv = function(a, b) + local q = a / b + if a >= 0 then + return math.floor(q) + else + return math.ceil(q) + end + end, + mod = function(a, b) + return a % b + end, + mul = function(a, b) + return a * b + end, + neq = function(a) + return -a + end, + unm = function(a) + return -a + end, -- an alias + pow = function(a, b) + return a ^ b + end, + sub = function(a, b) + return a - b + end, + truediv = function(a, b) + return a / b + end, + + ---------------------------------------------------------------------------- + -- String operators + ---------------------------------------------------------------------------- + concat = function(a, b) + return a .. b + end, + len = function(a) + return #a + end, + length = function(a) + return #a + end, -- an alias + + ---------------------------------------------------------------------------- + -- Logical operators + ---------------------------------------------------------------------------- + land = function(a, b) + return a and b + end, + lor = function(a, b) + return a or b + end, + lnot = function(a) + return not a + end, + truth = function(a) + return not not a + end, +} +exports.operator = operator +methods.operator = operator +exports.op = operator +methods.op = operator + +-------------------------------------------------------------------------------- +-- module definitions +-------------------------------------------------------------------------------- + +-- a special syntax sugar to export all functions to the global table +setmetatable(exports, { + __call = function(t, override) + for k, v in pairs(t) do + if rawget(_G, k) ~= nil then + local msg = 'function ' .. k .. ' already exists in global scope.' + if override then + rawset(_G, k, v) + print('WARNING: ' .. msg .. ' Overwritten.') + else + print('NOTICE: ' .. msg .. ' Skipped.') + end + else + rawset(_G, k, v) + end + end + end, +}) + +return exports diff --git a/framework/lualib/thirdparty/behavior3/behavior_node.lua b/framework/lualib/thirdparty/behavior3/behavior_node.lua new file mode 100755 index 0000000..b50f8ff --- /dev/null +++ b/framework/lualib/thirdparty/behavior3/behavior_node.lua @@ -0,0 +1,82 @@ +local bret = require 'behavior3.behavior_ret' +local process = require 'behavior3.sample_process' + +local sformat = string.format +local table = table +local print = print + +local mt = {} +mt.__index = mt + +local function new_node(...) + local obj = setmetatable({}, mt) + obj:init(...) + return obj +end + +function mt:init(node_data, tree) + self.tree = tree + self.name = node_data.name + self.id = node_data.id + self.info = sformat('node %s.%s %s', tree.name, self.id, self.name) + + self.data = node_data + self.args = self.data.args or {} + self.children = {} + for _, child_data in ipairs(node_data.children or {}) do + local child = new_node(child_data, tree) + table.insert(self.children, child) + end +end + +function mt:run(env) + --print("start", self.name, self.node_id) + if env:get_inner_var(self, "YIELD") == nil then + env:push_stack(self) + end + local vars = {} + for i, var_name in ipairs(self.data.input or {}) do + vars[i] = env:get_var(var_name) + end + local func = assert(process[self.name].run, self.name) + vars = table.pack(func(self, env, table.unpack(vars))) + local ret = vars[1] + assert(ret, self.info) + if ret ~= bret.RUNNING then + env:set_inner_var(self, "YIELD", nil) + env:pop_stack() + end + for i, var_name in ipairs(self.data.output or {}) do + env:set_var(var_name, vars[i + 1]) + end + env.last_ret = ret + --print("fini", self.name, self.node_id, table.unpack(vars)) + + if self.data.debug then + local var_str = '' + for k, v in pairs(env.vars) do + var_str = sformat("[%s]=%s,", k, v) + end + print(sformat("[DEBUG] btree:%s, node:%s, ret:%s vars:{%s}", + self.tree.name, self.id, ret, var_str)) + end + return ret +end + +function mt:yield(env, arg) + env:set_inner_var(self, "YIELD", arg or true) + return bret.RUNNING +end + +function mt:resume(env) + return env:get_inner_var(self, "YIELD"), env.last_ret +end + +local M = {} +function M.new(...) + return new_node(...) +end +function M.process(custom) + process = custom +end +return M diff --git a/framework/lualib/thirdparty/behavior3/behavior_ret.lua b/framework/lualib/thirdparty/behavior3/behavior_ret.lua new file mode 100755 index 0000000..645898d --- /dev/null +++ b/framework/lualib/thirdparty/behavior3/behavior_ret.lua @@ -0,0 +1,5 @@ +return { + FAIL = "FAIL", -- 失败 + SUCCESS = "SUCCESS", -- 成功 + RUNNING = "RUNNING", -- 正在运行 +} diff --git a/framework/lualib/thirdparty/behavior3/behavior_tree.lua b/framework/lualib/thirdparty/behavior3/behavior_tree.lua new file mode 100755 index 0000000..05fda32 --- /dev/null +++ b/framework/lualib/thirdparty/behavior3/behavior_tree.lua @@ -0,0 +1,115 @@ +local behavior_node = require 'behavior3.behavior_node' +local behavior_ret = require 'behavior3.behavior_ret' +local json = require 'json' + +local meta = { + __newindex = function(_, k) + error(string.format('readonly:%s', k), 2) + end +} +local function const(t) + setmetatable(t, meta) + for _, v in pairs(t) do + if type(v) == 'table' then + const(v) + end + end + return t +end + +local trees = {} + +local mt = {} +mt.__index = mt +function mt:init(name, path) + path = path or './workspace/trees/' + + self.name = name + self.tick = 0 + local file, err = io.open(string.format("%s%s%s", path, name, '.json'), 'r') + assert(file, err) + local str = file:read('*a') + local data = const(json.decode(str)) + self.root = behavior_node.new(data.root, self) +end + +function mt:run(env) + -- print(string.format('===== tree:%s, tick:%s, stack:%d =====', self.name, self.tick, #env.stack)) + if #env.stack > 0 then + local last_node = env.stack[#env.stack] + while last_node do + local ret = last_node:run(env) + if ret == behavior_ret.RUNNING then + break + end + last_node = env.stack[#env.stack] + end + else + self.root:run(env) + end + self.tick = self.tick + 1 +end + +function mt:interrupt(env) + if #env.stack > 0 then + env.inner_vars = {} + env.stack = {} + end +end + +local function new_tree(name) + local tree = setmetatable({}, mt) + tree:init(name) + trees[name] = tree + return tree +end + +local function new_env(params) + local env = { + inner_vars = {}, -- [k.."_"..node.id] => vars + vars = {}, + stack = {}, + last_ret = nil + } + for k, v in pairs(params) do + env[k] = v + end + + function env:get_var(k) + return env.vars[k] + end + function env:set_var(k, v) + self.vars[k] = v + end + function env:get_inner_var(node, k) + return self.inner_vars[k .. '_' .. node.id] + end + function env:set_inner_var(node, k, v) + self.inner_vars[k .. '_' .. node.id] = v + end + function env:push_stack(node) + self.stack[#self.stack + 1] = node + end + function env:pop_stack() + local node = self.stack[#self.stack] + self.stack[#self.stack] = nil + return node + end + return env +end + +local M = {} +function M.new(name, env_params) + local env = new_env(env_params) + local tree = trees[name] or new_tree(name) + return { + tree = tree, + run = function() + tree:run(env) + end, + interrupt = function() + tree:interrupt(env) + end, + } +end +return M diff --git a/framework/lualib/thirdparty/behavior3/nodes/actions/log.lua b/framework/lualib/thirdparty/behavior3/nodes/actions/log.lua new file mode 100755 index 0000000..07f0e94 --- /dev/null +++ b/framework/lualib/thirdparty/behavior3/nodes/actions/log.lua @@ -0,0 +1,20 @@ +-- Log +-- + +local bret = require "behavior3.behavior_ret" + +local M = { + name = "Log", + type = "Action", + desc = "打印日志", + args = { + {"str", "string", "日志"} + }, +} + +function M.run(node, _) + print(node.args.str) + return bret.SUCCESS +end + +return M diff --git a/framework/lualib/thirdparty/behavior3/nodes/actions/wait.lua b/framework/lualib/thirdparty/behavior3/nodes/actions/wait.lua new file mode 100755 index 0000000..c1ad203 --- /dev/null +++ b/framework/lualib/thirdparty/behavior3/nodes/actions/wait.lua @@ -0,0 +1,34 @@ +-- Wait +-- + +local bret = require 'behavior3.behavior_ret' + +local M = { + name = 'Wait', + type = 'Action', + desc = '等待', + args = { + {'time', 'int', '时间/tick'} + } +} + +-- local abs = math.abs +-- local SPEED = 50 + +function M.run(node, env) + local args = node.args + local t = node:resume(env) + if t then + if env.ctx.time >= t then + print('CONTINUE') + return bret.SUCCESS + else + print('WAITING') + return bret.RUNNING + end + end + print('Wait', args.time) + return node:yield(env, env.ctx.time + args.time) +end + +return M diff --git a/framework/lualib/thirdparty/behavior3/nodes/composites/parallel.lua b/framework/lualib/thirdparty/behavior3/nodes/composites/parallel.lua new file mode 100755 index 0000000..5abccb3 --- /dev/null +++ b/framework/lualib/thirdparty/behavior3/nodes/composites/parallel.lua @@ -0,0 +1,35 @@ +-- Parallel +-- + +local bret = require 'behavior3.behavior_ret' + +local M = { + name = 'Parallel', + type = 'Composite', + desc = '并行执行', + doc = [[ + 执行所有子节点并返回成功 + ]] +} +function M.run(node, env) + local last_idx, last_ret = node:resume(env) + if last_idx then + if last_ret == bret.RUNNING then + return last_ret + end + last_idx = last_idx + 1 + else + last_idx = 1 + end + + for i = last_idx, #node.children do + local child = node.children[i] + local r = child:run(env) + if r == bret.RUNNING then + return node:yield(env, i) + end + end + return bret.SUCCESS +end + +return M diff --git a/framework/lualib/thirdparty/behavior3/nodes/composites/selector.lua b/framework/lualib/thirdparty/behavior3/nodes/composites/selector.lua new file mode 100755 index 0000000..60a5ed3 --- /dev/null +++ b/framework/lualib/thirdparty/behavior3/nodes/composites/selector.lua @@ -0,0 +1,42 @@ +-- Selector +-- + +local bret = require 'behavior3.behavior_ret' + +local M = { + name = 'Selector', + type = 'Composite', + desc = '选择执行', + doc = [[ + + 一直往下执行,有子节点返回成功则返回成功,若全部节点返回失败则返回失败 + + 子节点是或的关系 + ]] +} +function M.run(node, env) + local last_idx, last_ret = node:resume(env) + if last_idx then + if last_ret == bret.SUCCESS or last_ret == bret.RUNNING then + return last_ret + elseif last_ret == bret.FAIL then + last_idx = last_idx + 1 + else + error('wrong ret') + end + else + last_idx = 1 + end + + for i = last_idx, #node.children do + local child = node.children[i] + local r = child:run(env) + if r == bret.RUNNING then + return node:yield(env, i) + end + if r == bret.SUCCESS then + return r + end + end + return bret.FAIL +end + +return M diff --git a/framework/lualib/thirdparty/behavior3/nodes/composites/sequence.lua b/framework/lualib/thirdparty/behavior3/nodes/composites/sequence.lua new file mode 100755 index 0000000..0fa51a1 --- /dev/null +++ b/framework/lualib/thirdparty/behavior3/nodes/composites/sequence.lua @@ -0,0 +1,44 @@ +-- Sequence +-- + +local bret = require 'behavior3.behavior_ret' + +local M = { + name = 'Sequence', + type = 'Composite', + desc = '顺序执行', + doc = [[ + + 一直往下执行,有子节点返回成功则返回成功,若全部节点返回失败则返回失败 + + 子节点是或的关系 + ]] +} + +function M.run(node, env) + local last_idx, last_ret = node:resume(env) + if last_idx then + -- print("last", last_idx, last_ret) + if last_ret == bret.FAIL or bret.RUNNING then + return last_ret + elseif last_ret == bret.SUCCESS then + last_idx = last_idx + 1 + else + error('wrong ret') + end + else + last_idx = 1 + end + + for i = last_idx, #node.children do + local child = node.children[i] + local r = child:run(env) + if r == bret.RUNNING then + return node:yield(env, i) + end + if r == bret.FAIL then + return r + end + end + return bret.SUCCESS +end + +return M diff --git a/framework/lualib/thirdparty/behavior3/nodes/conditions/cmp.lua b/framework/lualib/thirdparty/behavior3/nodes/conditions/cmp.lua new file mode 100755 index 0000000..29ed5ae --- /dev/null +++ b/framework/lualib/thirdparty/behavior3/nodes/conditions/cmp.lua @@ -0,0 +1,46 @@ +-- Cmp + +local bret = require 'behavior3.behavior_ret' + +local M = { + name = 'Cmp', + type = 'Condition', + desc = '比较值大小', + args = { + {'value', 'lua?', '值'}, + {'gt', 'int?', '>'}, + {'ge', 'int?', '>='}, + {'eq', 'int?', '=='}, + {'le', 'int?', '<='}, + {'lt', 'int?', '<'} + }, + input = {'值(int)'}, + doc = [[ + + 若值为空,返回失败 + + 非整数类型可能会报错 + ]] +} + +local function ret(r) + return r and bret.SUCCESS or bret.FAIL +end + +function M.run(node, _, value) + assert(type(value) == 'number') + local args = node.args + if args.gt then + return ret(value > args.gt) + elseif args.ge then + return ret(value >= args.ge) + elseif args.eq then + return ret(value == args.eq) + elseif args.lt then + return ret(value < args.lt) + elseif args.le then + return ret(value <= args.le) + else + error('args error') + end +end + +return M \ No newline at end of file diff --git a/framework/lualib/thirdparty/behavior3/nodes/decorators/always_fail.lua b/framework/lualib/thirdparty/behavior3/nodes/decorators/always_fail.lua new file mode 100755 index 0000000..32175f9 --- /dev/null +++ b/framework/lualib/thirdparty/behavior3/nodes/decorators/always_fail.lua @@ -0,0 +1,31 @@ +-- AlwaysSuccess +-- + +local bret = require 'behavior3.behavior_ret' + +local M = { + name = 'AlwaysFail', + type = 'Decorator', + desc = '始终返回失败', + doc = [[ + + 只能有一个子节点,多个仅执行第一个 + + 不管子节点是否成功都返回失败 + ]] +} +function M.run(node, env, _) + local yeild, last_ret = node:resume(env) + if yeild then + if last_ret == bret.RUNNING then + return last_ret + end + return bret.FAIL + end + + local r = node.children[1]:run(env) + if r == bret.RUNNING then + return node:yeild(env) + end + return bret.FAIL +end + +return M diff --git a/framework/lualib/thirdparty/behavior3/nodes/decorators/always_success.lua b/framework/lualib/thirdparty/behavior3/nodes/decorators/always_success.lua new file mode 100755 index 0000000..73b8636 --- /dev/null +++ b/framework/lualib/thirdparty/behavior3/nodes/decorators/always_success.lua @@ -0,0 +1,32 @@ +-- AlwaysSuccess +-- + +local bret = require 'behavior3.behavior_ret' + +local M = { + name = 'AlwaysSuccess', + type = 'Decorator', + desc = '始终返回成功', + doc = [[ + + 只能有一个子节点,多个仅执行第一个 + + 不管子节点是否成功都返回成功 + ]] +} + +function M.run(node, env) + local yeild, last_ret = node:resume(env) + if yeild then + if last_ret == bret.RUNNING then + return last_ret + end + return bret.SUCCESS + end + + local r = node.children[1]:run(env) + if r == bret.RUNNING then + return node:yeild(env) + end + return bret.SUCCESS +end + +return M diff --git a/framework/lualib/thirdparty/behavior3/nodes/decorators/not.lua b/framework/lualib/thirdparty/behavior3/nodes/decorators/not.lua new file mode 100755 index 0000000..2d456f9 --- /dev/null +++ b/framework/lualib/thirdparty/behavior3/nodes/decorators/not.lua @@ -0,0 +1,33 @@ +-- Not +-- + +local bret = require 'behavior3.behavior_ret' + +local M = { + name = 'Not', + type = 'Decorator', + desc = '取反', + doc = [[ + + 将子节点的返回值取反 + ]] +} + +function M.run(node, env) + -- local yield = node:resume(env) + local r + if node:resume(env) then + r = env.last_ret + else + r = node.children[1]:run(env) + end + + if r == bret.SUCCESS then + return bret.FAIL + elseif r == bret.FAIL then + return bret.SUCCESS + elseif r == bret.RUNNING then + return node:yield(env) + end +end + +return M diff --git a/framework/lualib/thirdparty/behavior3/sample_process.lua b/framework/lualib/thirdparty/behavior3/sample_process.lua new file mode 100755 index 0000000..065432c --- /dev/null +++ b/framework/lualib/thirdparty/behavior3/sample_process.lua @@ -0,0 +1,18 @@ +return { + -- 复合节点 + Parallel = require "behavior3.nodes.composites.parallel", + Selector = require "behavior3.nodes.composites.selector", + Sequence = require "behavior3.nodes.composites.sequence", + + -- 装饰节点 + Not = require "behavior3.nodes.decorators.not", + AlwaysFail = require "behavior3.nodes.decorators.always_fail", + AlwaysSuccess = require "behavior3.nodes.decorators.always_success", + + -- 条件节点 + Cmp = require "behavior3.nodes.conditions.cmp", + + -- 行为节点 + Log = require "behavior3.nodes.actions.log", + Wait = require "behavior3.nodes.actions.wait", +} \ No newline at end of file diff --git a/framework/lualib/thirdparty/beholder/beholder.lua b/framework/lualib/thirdparty/beholder/beholder.lua new file mode 100644 index 0000000..7a6a306 --- /dev/null +++ b/framework/lualib/thirdparty/beholder/beholder.lua @@ -0,0 +1,186 @@ +-- beholder.lua - v2.1.1 (2011-11) +-- Copyright (c) 2011 Enrique García Cota +-- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +-- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN callback OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +local function copy(t) + local c = {} + for i = 1, #t do + c[i] = t[i] + end + return c +end + +local function hash2array(t) + local arr, i = {}, 0 + for _, v in pairs(t) do + i = i + 1 + arr[i] = v + end + return arr, i +end +-- private Node class + +local nodesById = nil +local root = nil + +local function newNode() + return { + callbacks = {}, + children = setmetatable({}, { + __mode = "k", + }), + } +end + +local function findNodeById(id) + return nodesById[id] +end + +local function findOrCreateChildNode(self, key) + self.children[key] = self.children[key] or newNode() + return self.children[key] +end + +local function findOrCreateDescendantNode(self, keys) + local node = self + for i = 1, #keys do + node = findOrCreateChildNode(node, keys[i]) + end + return node +end + +local function invokeNodeCallbacks(self, params) + -- copy the hash into an array, for safety (self-erasures) + local callbacks, count = hash2array(self.callbacks) + for i = 1, #callbacks do + callbacks[i](unpack(params)) + end + return count +end + +local function invokeAllNodeCallbacksInSubTree(self, params) + local counter = invokeNodeCallbacks(self, params) + for _, child in pairs(self.children) do + counter = counter + invokeAllNodeCallbacksInSubTree(child, params) + end + return counter +end + +local function invokeNodeCallbacksFromPath(self, path) + local node = self + local params = copy(path) + local counter = invokeNodeCallbacks(node, params) + + for i = 1, #path do + node = node.children[path[i]] + if not node then + break + end + table.remove(params, 1) + counter = counter + invokeNodeCallbacks(node, params) + end + + return counter +end + +local function addCallbackToNode(self, callback) + local id = {} + self.callbacks[id] = callback + nodesById[id] = self + return id +end + +local function removeCallbackFromNode(self, id) + self.callbacks[id] = nil + nodesById[id] = nil +end + +------ beholder table + +local beholder = {} + +-- beholder private functions/vars + +local groups = nil +local currentGroupId = nil + +local function addIdToCurrentGroup(id) + if currentGroupId then + groups[currentGroupId] = groups[currentGroupId] or setmetatable({}, { + __mode = "k", + }) + local group = groups[currentGroupId] + group[#group + 1] = id + end + return id +end + +local function stopObservingGroup(group) + local count = #group + for i = 1, count do + beholder.stopObserving(group[i]) + end + return count +end + +local function falseIfZero(n) + return n > 0 and n +end + +local function extractEventAndCallbackFromParams(params) + assert(#params > 0, + "beholder.observe requires at least one parameter - the callback. You usually want to use two, i.e.: beholder.observe('EVENT', callback)") + local callback = table.remove(params, #params) + return params, callback +end + +------ Public interface + +function beholder.observe(...) + local event, callback = extractEventAndCallbackFromParams({...}) + local node = findOrCreateDescendantNode(root, event) + return addIdToCurrentGroup(addCallbackToNode(node, callback)) +end + +function beholder.stopObserving(id) + local node = findNodeById(id) + if node then + removeCallbackFromNode(node, id) + end + + local group, count = groups[id], 0 + if group then + count = stopObservingGroup(group) + end + + return (node or count > 0) and true or false +end + +function beholder.group(groupId, f) + assert(not currentGroupId, "beholder.group can not be nested!") + currentGroupId = groupId + f() + currentGroupId = nil +end + +function beholder.trigger(...) + return falseIfZero(invokeNodeCallbacksFromPath(root, {...})) +end + +function beholder.triggerAll(...) + return falseIfZero(invokeAllNodeCallbacksInSubTree(root, {...})) +end + +function beholder.reset() + root = newNode() + nodesById = setmetatable({}, { + __mode = "k", + }) + groups = {} + currentGroupId = nil +end + +beholder.reset() + +return beholder diff --git a/framework/lualib/thirdparty/events.lua b/framework/lualib/thirdparty/events.lua new file mode 100644 index 0000000..e24e2bd --- /dev/null +++ b/framework/lualib/thirdparty/events.lua @@ -0,0 +1,221 @@ +------------------------------------------------------------------------------ +-- https://github.com/simenkid/lua-events +------------------------------------------------------------------------------ +local PFX = '__lsn_' +local PFX_LEN = #PFX +local Events = { + defaultMaxListeners = 10, +} + +setmetatable(Events, { + __call = function(_, ...) + return Events:new(...) + end, +}) + +local function rmEntry(tbl, pred) + local x, len = 0, #tbl + for i = 1, len do + local trusy, idx = false, (i - x) + if (type(pred) == 'function') then + trusy = pred(tbl[idx]) + else + trusy = tbl[idx] == pred + end + + if (tbl[idx] ~= nil and trusy) then + tbl[idx] = nil + table.remove(tbl, idx) + x = x + 1 + end + end + return tbl +end + +function Events:new(obj) + obj = obj or {} + self.__index = self + setmetatable(obj, self) + obj._on = {} + + return obj +end + +function Events:evTable(ev) + if (type(self._on[ev]) ~= 'table') then + self._on[ev] = {} + end + return self._on[ev] +end + +function Events:getEvTable(ev) + return self._on[ev] +end + +-- ************************************************************************ -- +-- ** Public APIs * -- +-- ************************************************************************ -- +function Events:addListener(ev, listener) + local pfx_ev = PFX .. tostring(ev) + local evtbl = self:evTable(pfx_ev) + local maxLsnNum = self.currentMaxListeners or self.defaultMaxListeners + local lsnNum = self:listenerCount(ev) + table.insert(evtbl, listener) + + if (lsnNum > maxLsnNum) then + print('WARN: Number of ' .. string.sub(pfx_ev, PFX_LEN + 1) .. " event listeners: " .. tostring(lsnNum)) + end + return self +end + +function Events:emit(ev, ...) + local pfx_ev = PFX .. tostring(ev) + local evtbl = self:getEvTable(pfx_ev) + if (evtbl ~= nil) then + for _, lsn in ipairs(evtbl) do + local status, err = pcall(lsn, ...) + if not (status) then + print(string.sub(_, PFX_LEN + 1) .. " emit error: " .. tostring(err)) + end + end + end + + -- one-time listener + pfx_ev = pfx_ev .. ':once' + evtbl = self:getEvTable(pfx_ev) + + if (evtbl ~= nil) then + for _, lsn in ipairs(evtbl) do + local status, err = pcall(lsn, ...) + if not (status) then + print(string.sub(_, PFX_LEN + 1) .. " emit error: " .. tostring(err)) + end + end + + rmEntry(evtbl, function(v) + return v ~= nil + end) + self._on[pfx_ev] = nil + end + return self +end + +function Events:getMaxListeners() + return self.currentMaxListeners or self.defaultMaxListeners +end + +function Events:listenerCount(ev) + local totalNum = 0 + local pfx_ev = PFX .. tostring(ev) + local evtbl = self:getEvTable(pfx_ev) + + if (evtbl ~= nil) then + totalNum = totalNum + #evtbl + end + + pfx_ev = pfx_ev .. ':once' + evtbl = self:getEvTable(pfx_ev) + + if (evtbl ~= nil) then + totalNum = totalNum + #evtbl + end + + return totalNum +end + +function Events:listeners(ev) + local pfx_ev = PFX .. tostring(ev) + local evtbl = self:getEvTable(pfx_ev) + local clone = {} + + if (evtbl ~= nil) then + for i, lsn in ipairs(evtbl) do + table.insert(clone, lsn) + end + end + + pfx_ev = pfx_ev .. ':once' + evtbl = self:getEvTable(pfx_ev) + + if (evtbl ~= nil) then + for i, lsn in ipairs(evtbl) do + table.insert(clone, lsn) + end + end + + return clone +end + +Events.on = Events.addListener + +function Events:once(ev, listener) + local pfx_ev = PFX .. tostring(ev) .. ':once' + local evtbl = self:evTable(pfx_ev) + local maxLsnNum = self.currentMaxListeners or self.defaultMaxListeners + local lsnNum = self:listenerCount(ev) + if (lsnNum > maxLsnNum) then + print('WARN: Number of ' .. ev .. " event listeners: " .. tostring(lsnNum)) + end + + table.insert(evtbl, listener) + return self +end + +function Events:removeAllListeners(ev) + if ev ~= nil then + local pfx_ev = PFX .. tostring(ev) + local evtbl = self:evTable(pfx_ev) + rmEntry(evtbl, function(v) + return v ~= nil + end) + + pfx_ev = pfx_ev .. ':once' + evtbl = self:evTable(pfx_ev) + rmEntry(evtbl, function(v) + return v ~= nil + end) + self._on[pfx_ev] = nil + else + for _pfx_ev, _t in pairs(self._on) do + self:removeAllListeners(string.sub(_pfx_ev, PFX_LEN + 1)) + end + end + + for _pfx_ev, _t in pairs(self._on) do + if (#_t == 0) then + self._on[_pfx_ev] = nil + end + end + + return self +end + +function Events:removeListener(ev, listener) + local pfx_ev = PFX .. tostring(ev) + local evtbl = self:evTable(pfx_ev) + local lsnCount = 0 + assert(listener ~= nil, "listener is nil") + -- normal listener + rmEntry(evtbl, listener) + + if (#evtbl == 0) then + self._on[pfx_ev] = nil + end + + -- emit-once listener + pfx_ev = pfx_ev .. ':once' + evtbl = self:evTable(pfx_ev) + rmEntry(evtbl, listener) + + if (#evtbl == 0) then + self._on[pfx_ev] = nil + end + return self +end + +function Events:setMaxListeners(n) + self.currentMaxListeners = n + return self +end + +return Events diff --git a/framework/lualib/thirdparty/fsm.lua b/framework/lualib/thirdparty/fsm.lua new file mode 100644 index 0000000..35e0282 --- /dev/null +++ b/framework/lualib/thirdparty/fsm.lua @@ -0,0 +1,221 @@ +-- https://github.com/unindented/lua-fsm + +local unpack = unpack or table.unpack +local type = type +local assert = assert +local ipairs = ipairs +local pairs = pairs +local tinsert = table.insert +------------------------------------------- + +local M = {} + +M.WILDCARD = "*" +M.DEFERRED = 0 +M.SUCCEEDED = 1 +M.NO_TRANSITION = 2 +M.PENDING = 3 +M.CANCELLED = 4 + +local function do_callback(handler, args) + if handler then + return handler(unpack(args)) + end +end + +local function before_event(self, event, _, _, args) + local specific = do_callback(self["on_before_" .. event], args) + local general = do_callback(self["on_before_event"], args) + + if specific == false or general == false then + return false + end +end + +local function leave_state(self, _, from, _, args) + local specific = do_callback(self["on_leave_" .. from], args) + local general = do_callback(self["on_leave_state"], args) + + if specific == false or general == false then + return false + end + if specific == M.DEFERRED or general == M.DEFERRED then + return M.DEFERRED + end +end + +local function enter_state(self, _, _, to, args) + do_callback(self["on_enter_" .. to] or self["on_" .. to], args) + do_callback(self["on_enter_state"] or self["on_state"], args) +end + +local function after_event(self, event, _, _, args) + do_callback(self["on_after_" .. event] or self["on_" .. event], args) + do_callback(self["on_after_event"] or self["on_event"], args) +end + +local function build_transition(self, event, states) + return function(...) + local from = self.current + local to = states[from] or states[M.WILDCARD] or from + local args = {self, event, from, to, ...} + + assert(not self.is_pending(), "previous transition still pending") + + assert(self.can(event), "invalid transition from state '" .. from .. "' with event '" .. event .. "'") + + local before = before_event(self, event, from, to, args) + if before == false then + return M.CANCELLED + end + + if from == to then + after_event(self, event, from, to, args) + return M.NO_TRANSITION + end + + self.confirm = function() + self.confirm = nil + self.cancel = nil + + self.current = to + + enter_state(self, event, from, to, args) + after_event(self, event, from, to, args) + + return M.SUCCEEDED + end + + self.cancel = function() + self.confirm = nil + self.cancel = nil + + after_event(self, event, from, to, args) + + return M.CANCELLED + end + + local leave = leave_state(self, event, from, to, args) + if leave == false then + return M.CANCELLED + end + if leave == M.DEFERRED then + return M.PENDING + end + + if self.confirm then + return self.confirm() + end + end +end + +function M.create(cfg, target) + local self = target or {} + + -- Initial state. + local initial = cfg.initial + -- Allow for a string, or a map like `{state = "foo", event = "setup"}`. + initial = type(initial) == "string" and { + state = initial, + } or initial + + -- Initial event. + local initial_event = initial and initial.event or "startup" + + -- Terminal state. + local terminal = cfg.terminal + + -- Events. + local events = cfg.events or {} + + -- Callbacks. + local callbacks = cfg.callbacks or {} + + -- Track state transitions allowed for an event. + local states_for_event = {} + + -- Track events allowed from a state. + local events_for_state = {} + + local function add(e) + -- Allow wildcard transition if `from` is not specified. + local from = type(e.from) == "table" and e.from or (e.from and {e.from} or {M.WILDCARD}) + local to = e.to + local event = e.name + + states_for_event[event] = states_for_event[event] or {} + for _, fr in ipairs(from) do + events_for_state[fr] = events_for_state[fr] or {} + tinsert(events_for_state[fr], event) + + -- Allow no-op transition if `to` is not specified. + states_for_event[event][fr] = to or fr + end + end + + if initial then + add({ + name = initial_event, + from = "none", + to = initial.state, + }) + end + + for _, event in ipairs(events) do + add(event) + end + + for event, states in pairs(states_for_event) do + self[event] = build_transition(self, event, states) + end + + for name, callback in pairs(callbacks) do + self[name] = callback + end + + self.current = "none" + + function self.is(state) + if type(state) == "table" then + for _, s in ipairs(state) do + if self.current == s then + return true + end + end + + return false + end + + return self.current == state + end + + function self.can(event) + local states = states_for_event[event] + local to = states[self.current] or states[M.WILDCARD] + return to ~= nil + end + + function self.cannot(event) + return not self.can(event) + end + + function self.transitions() + return events_for_state[self.current] + end + + function self.is_pending() + return self.confirm ~= nil + end + + function self.is_finished() + return self.is(terminal) + end + + if initial and not initial.defer then + self[initial_event]() + end + + return self +end + +return M diff --git a/framework/lualib/thirdparty/hashids.lua b/framework/lualib/thirdparty/hashids.lua new file mode 100644 index 0000000..acc0c06 --- /dev/null +++ b/framework/lualib/thirdparty/hashids.lua @@ -0,0 +1,254 @@ +-- 数字编码库 +-- https://github.com/leihog/hashids.lua +local ceil = math.ceil +local floor = math.floor +local pow = math.pow +local substr = string.sub +local upcase = string.upper +local format = string.format +local strcat = table.concat +local push = table.insert +local unpack = table.unpack or unpack + +local str_switch_pos = function(str, pos1, pos2) + pos1 = pos1 + 1; + pos2 = pos2 + 1; + local a, b = str:sub(pos1, pos1), str:sub(pos2, pos2); + if pos1 > pos2 then + return str:gsub(a, b, 1):gsub(b, a, 1); + end + return str:gsub(b, a, 1):gsub(a, b, 1); +end + +local hash_mt = {}; +hash_mt.__index = hash_mt; + +local function gcap(str, pos) + pos = pos + 1; + return str:sub(pos, pos); +end + +-- TODO using string concatenation with .. might not be the fastest in a loop +local function hash(number, alphabet) + local hash, alen = "", alphabet:len(); + repeat + hash = gcap(alphabet, (number % alen)) .. hash; + number = floor(number / alen); + until number == 0 + + return hash; +end + +local function unhash(input, alphabet) + local number, ilen, alen = 0, input:len(), alphabet:len(); + + for i = 0, ilen do + local cpos = (alphabet:find(gcap(input, i), 1, true) - 1); + number = number + cpos * pow(alen, (ilen - i - 1)) + end + + return number; +end + +local function consistent_shuffle(alphabet, salt) + local slen = salt:len(); + if slen == 0 then + return alphabet + end + + local v, p = 0, 0; + for i = (alphabet:len() - 1), 1, -1 do + v = (v % slen); + local ord = gcap(salt, v):byte(); + p = p + ord; + local j = (ord + v + p) % i; + + alphabet = str_switch_pos(alphabet, j, i); + v = v + 1; + end + + return alphabet; +end + +function hash_mt:encode(...) + local numbers = {select(1, ...)}; + if #numbers == 0 then + return "" + end + local numbers_size, hash_int = #numbers, 0; + + for i, number in ipairs(numbers) do + assert(type(number) == 'number', "all paramters must be numbers"); + hash_int = hash_int + (number % ((i - 1) + 100)); + end + + local alpha = self.alphabet; + local alpha_len = alpha:len(); + + local lottery = gcap(alpha, hash_int % alpha_len); + local ret = lottery; + local last = nil; + + for i, number in ipairs(numbers) do + -- for i=1, #numbers do + -- local number = numbers[i]; + alpha = consistent_shuffle(alpha, substr(strcat({lottery, self.salt, alpha}), 1, alpha_len)); + last = hash(number, alpha); + ret = ret .. last; + + if i < numbers_size then + number = number % (last:byte() + (i - 1)); + ret = ret .. gcap(self.seps, (number % self.seps:len())); + end + end + + local guards_len = self.guards:len(); + if ret:len() < self.min_hash_length then + local guard_index = (hash_int + gcap(ret, 0):byte()) % guards_len; + ret = gcap(self.guards, guard_index) .. ret; + + if ret:len() < self.min_hash_length then + guard_index = (hash_int + gcap(ret, 2):byte()) % guards_len; + ret = ret .. gcap(self.guards, guard_index); + end + end + + local half_len, excess = floor(alpha_len * 0.5), 0; -- alpha_len / 2 + while ret:len() < self.min_hash_length do + alpha = consistent_shuffle(alpha, alpha); + ret = alpha:sub(half_len + 1) .. ret .. alpha:sub(1, half_len); + + excess = (ret:len() - self.min_hash_length); + if excess > 0 then + excess = (excess * 0.5); + ret = ret:sub(floor(excess + 1), floor(excess + self.min_hash_length)); + end + end + + return ret; +end + +function hash_mt:encode_hex(str) + if str:match("%X") then + return "" + end + local pos, max, numbers = 0, #str, {} + while true do + local part = substr(str, pos + 1, pos + 12) + if part == "" then + break + end + pos = pos + #part + push(numbers, tonumber("1" .. part, 16)) + end + + return self:encode(unpack(numbers)) +end + +function hash_mt:decode(hash) + -- TODO validate input + + local parts, index = {}, 1; + for part in hash:gmatch("[^" .. self.guards .. "]+") do + parts[index] = part; + index = index + 1; + end + + local num_parts, t, lottery = #parts; + if num_parts == 3 or num_parts == 2 then + t = parts[2]; + else + t = parts[1]; + end + + lottery = gcap(t, 0); -- put the first char in lottery + t = t:sub(2); -- then put the rest in t + + parts, index = {}, 1; + for part in t:gmatch("[^" .. self.seps .. "]+") do + parts[index] = part; + index = index + 1; + end + + local ret, alpha = {}, self.alphabet; + for i = 1, #parts do + alpha = consistent_shuffle(alpha, substr(strcat({lottery, self.salt, alpha}), 1, self.alphabet_length)); + ret[i] = unhash(parts[i], alpha); + end + + return ret; +end + +function hash_mt:decode_hex(hash) + local result, numbers = {}, self:decode(hash) + for _, number in ipairs(numbers) do + push(result, substr(format("%x", number), 2)) + end + + return upcase(strcat(result)) +end + +return { + VERSION = "1.0.6", + new = function(salt, min_hash_length, alphabet) + salt = salt or ""; + min_hash_length = min_hash_length or 0; + alphabet = alphabet or "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + -- TODO make sure alphabet doesn't contain duplicates. + + local tmp_seps, tmp_alpha, c = "", ""; + local seps = "cfhistuCFHISTU"; + + for i = 1, alphabet:len() do + c = alphabet:sub(i, i); + + if seps:find(c, 1, true) then + tmp_seps = tmp_seps .. c; + else + tmp_alpha = tmp_alpha .. c; + end + end + + seps = consistent_shuffle(tmp_seps, salt); + alphabet = tmp_alpha; + + -- constants + local SEPS_DIV = 3.5; + local GUARD_DIV = 12; + + if seps:len() == 0 or (alphabet:len() / seps:len()) > SEPS_DIV then + local seps_len = floor(ceil(alphabet:len() / SEPS_DIV)); + if seps_len == 1 then + seps_len = 2 + end + + if seps_len > seps:len() then + local diff = seps_len - seps:len(); + seps = seps .. alphabet:sub(1, diff); + alphabet = alphabet:sub(diff + 1); + else + seps = seps:sub(1, seps_len); + end + end + + alphabet = consistent_shuffle(alphabet, salt); + local guards = ""; + local guard_count = ceil(alphabet:len() / GUARD_DIV); + if alphabet:len() < 3 then + guards = seps:sub(1, guard_count); + seps = seps:sub(guard_count + 1); + else + guards = alphabet:sub(1, guard_count); + alphabet = alphabet:sub(guard_count + 1); + end + + local obj = { + salt = salt, + alphabet = alphabet, + seps = seps, + guards = guards, + min_hash_length = min_hash_length, + }; + return setmetatable(obj, hash_mt); + end, +} diff --git a/framework/lualib/thirdparty/hotfix/helper/hotfix_helper.lua b/framework/lualib/thirdparty/hotfix/helper/hotfix_helper.lua new file mode 100644 index 0000000..93edc16 --- /dev/null +++ b/framework/lualib/thirdparty/hotfix/helper/hotfix_helper.lua @@ -0,0 +1,101 @@ +--- Hotfix helper which hotfixes modified modules. +-- Using lfs to detect files' modification. + +local M = {} + +local lfs = require("lfs") +local hotfix = require("hotfix.hotfix") +local skynet = require("skynet") + +-- Map file path to file time to detect modification. +local path_to_time = {} + +-- global_objects which must not hotfix. +local global_objects = { + arg, + assert, + collectgarbage, + coroutine, + debug, + dofile, + error, + getmetatable, + io, + ipairs, + load, + loadfile, + math, + next, + os, + package, + pairs, + pcall, + print, + rawequal, + rawget, + rawlen, + rawset, + require, + select, + setmetatable, + string, + table, + tonumber, + tostring, + type, + utf8, + xpcall, + lfs, + skynet, -- skynet 模块不能够热更 +} + +--- Check modules and hotfix. +local hotfix_module_names = require("hotfix.helper.hotfix_module_names") + +local function tcontains(t, value) + for _, v in pairs(t) do + if (v == value) then + return true + end + end + return false +end + +function M.add(module_name) + if not tcontains(hotfix_module_names, module_name) then + -- 保证不重复 + table.insert(hotfix_module_names, module_name) + end +end + +function M.check() + for _, module_name in pairs(hotfix_module_names) do + -- skynet.error("hotfix module_name:", module_name) + + local path, err = package.searchpath(module_name, package.path) + -- Skip non-exist module. + if not path then + skynet.error(string.format("No such module: %s. %s", module_name, err)) + goto continue + end + + local file_time = lfs.attributes(path, "modification") + if file_time == path_to_time[path] then + goto continue + end + + skynet.error(string.format("Hot fix module %s (%s)", module_name, path)) + path_to_time[path] = file_time + hotfix.hotfix_module(module_name) + ::continue:: + end -- for +end -- check() + +function M.init() + hotfix.log_debug = function(s) + skynet.error(s) + end + hotfix.add_protect(global_objects) +end + +return M diff --git a/framework/lualib/thirdparty/hotfix/helper/hotfix_module_names.lua b/framework/lualib/thirdparty/hotfix/helper/hotfix_module_names.lua new file mode 100644 index 0000000..ef9f0c3 --- /dev/null +++ b/framework/lualib/thirdparty/hotfix/helper/hotfix_module_names.lua @@ -0,0 +1,12 @@ +-- Module names need hotfix. +-- hotfix_helper.lua will reload this module in check(). +-- So it can be changed dynamically. + +local hotfix_module_names = { + --"node.game.service.agent.module.build.buildCtrl", + --"node.game.service.agent.module.queue.queueCtrl", + --"node.game.service.agent.module.itemBag.itemBagCtrl", + --"node.game.service.agent.module.army.armyCtrl" +} + +return hotfix_module_names diff --git a/framework/lualib/thirdparty/hotfix/hotfix.lua b/framework/lualib/thirdparty/hotfix/hotfix.lua new file mode 100644 index 0000000..0fc0f41 --- /dev/null +++ b/framework/lualib/thirdparty/hotfix/hotfix.lua @@ -0,0 +1,98 @@ +--[[ +Lua 5.2/5.3 hotfix. Hot update functions and keep old data. +Author: Jin Qing ( http://blog.csdn.net/jq0123 ) +--]] +local M = {} + +local module_updater = require("hotfix.internal.module_updater") +local functions_replacer = require("hotfix.internal.functions_replacer") + +-- Do not update and replace protected objects. +local protected = {} + +-- To protect self. +local function add_self_to_protect() + M.add_protect { + M, + M.hotfix_module, + M.log_error, + M.log_info, + M.log_debug, + M.add_protect, + M.remove_protect, + module_updater, + module_updater.log_debug, + module_updater.update_loaded_module, + functions_replacer, + functions_replacer.replace_all + } +end -- add_self_to_protect + +-- Hotfix module with new module object. +-- Update package.loaded[module_name] and replace all functions. +-- module_obj is the newly loaded module object. +local function hotfix_module_with_obj(module_name, module_obj) + assert("string" == type(module_name)) + add_self_to_protect() + module_updater.log_debug = M.log_debug + + -- Step 1: Update package.loaded[module_name], recording updated functions. + local updated_function_map = module_updater.update_loaded_module(module_name, protected, module_obj) + -- Step 2: Replace old functions with new ones in module_obj, _G and registry. + functions_replacer.replace_all(protected, updated_function_map, module_obj) +end -- hotfix_module_with_obj() + +-- Hotfix module. +-- Skip unloaded module. +-- Usage: hotfix_module("mymodule.sub_module") +-- Returns package.loaded[module_name]. +function M.hotfix_module(module_name) + assert("string" == type(module_name)) + if not package.loaded[module_name] then + M.log_debug("Skip unloaded module: " .. module_name) + return package.loaded[module_name] + end + M.log_debug("Hot fix module: " .. module_name) + + local file_path = assert(package.searchpath(module_name, package.path)) + local fp = assert(io.open(file_path)) + local chunk = fp:read("*all") + fp:close() + + -- Load chunk. + local func = assert(load(chunk, "@" .. file_path)) + local ok, obj = assert(pcall(func)) + if nil == obj then + obj = true + end -- obj may be false + + hotfix_module_with_obj(module_name, obj) + return package.loaded[module_name] +end + +-- User can set log functions. Default is no log. +-- Like: require("hotfix").log_info = function(s) mylog:info(s) end +function M.log_error(msg_str) +end +function M.log_info(msg_str) +end +function M.log_debug(msg_str) +end + +-- Add objects to protect. +-- Example: add_protect({table, math, print}) +function M.add_protect(object_array) + for _, obj in pairs(object_array) do + protected[obj] = true + end +end -- add_protect() + +-- Remove objects in protected set. +-- Example: remove_protect({table, math, print}) +function M.remove_protect(object_array) + for _, obj in pairs(object_array) do + protected[obj] = nil + end +end -- remove_protect() + +return M diff --git a/framework/lualib/thirdparty/hotfix/internal/functions_replacer.lua b/framework/lualib/thirdparty/hotfix/internal/functions_replacer.lua new file mode 100644 index 0000000..4af9955 --- /dev/null +++ b/framework/lualib/thirdparty/hotfix/internal/functions_replacer.lua @@ -0,0 +1,119 @@ +--- Replace functions of table or upvalue. +-- Search for the old functions and replace them with new ones. +local M = {} + +-- Objects whose functions have been replaced already. +-- Each objects need to be replaced once. +local replaced_obj = {} + +-- Map old functions to new functions. +-- Used to replace functions finally. +-- Set to hotfix.updated_func_map. +local updated_func_map = {} + +-- Do not update and replace protected objects. +-- Set to hotfix.protected. +local protected = {} + +local replace_functions -- forward declare + +-- Replace all updated functions in upvalues of function object. +local function replace_functions_in_upvalues(function_object) + local obj = function_object + assert("function" == type(obj)) + assert(not protected[obj]) + assert(obj ~= updated_func_map) + + for i = 1, math.huge do + local name, value = debug.getupvalue(obj, i) + if not name then + return + end + local new_func = updated_func_map[value] + if new_func then + assert("function" == type(value)) + debug.setupvalue(obj, i, new_func) + else + replace_functions(value) + end + end -- for + assert(false, "Can not reach here!") +end -- replace_functions_in_upvalues() + +-- Replace all updated functions in the table. +local function replace_functions_in_table(table_object) + local obj = table_object + assert("table" == type(obj)) + assert(not protected[obj]) + assert(obj ~= updated_func_map) + + replace_functions(debug.getmetatable(obj)) + local new = {} -- to assign new fields + for k, v in pairs(obj) do + local new_k = updated_func_map[k] + local new_v = updated_func_map[v] + if new_k then + obj[k] = nil -- delete field + new[new_k] = new_v or v + else + -- obj[k] = new_v or v + local ok, err = pcall(rawset, obj, k, new_v or v) + if not ok then + -- print("replaced failed:", k, err) + end + replace_functions(k) + end + if not new_v then + replace_functions(v) + end + end -- for k, v + for k, v in pairs(new) do + obj[k] = v + end +end -- replace_functions_in_table() + +-- Replace all updated functions. +-- Record all replaced objects in replaced_obj. +function replace_functions(obj) + if protected[obj] then + return + end + local obj_type = type(obj) + if "function" ~= obj_type and "table" ~= obj_type then + return + end + if replaced_obj[obj] then + return + end + replaced_obj[obj] = true + assert(obj ~= updated_func_map) + + if "function" == obj_type then + replace_functions_in_upvalues(obj) + else -- table + replace_functions_in_table(obj) + end +end -- replace_functions(obj) + +--- Replace all old functions with new ones. +-- Replace in new_obj, _G and debug.getregistry(). +-- a_protected is a list of protected object. +-- an_updated_func_map is a map from old function to new function. +-- new_obj is the newly loaded module. +function M.replace_all(a_protected, an_updated_func_map, new_obj) + protected = a_protected + updated_func_map = an_updated_func_map + assert(type(protected) == "table") + assert(type(updated_func_map) == "table") + if nil == next(updated_func_map) then + return + end + + replaced_obj = {} + replace_functions(new_obj) -- new_obj may be not in _G + replace_functions(_G) + replace_functions(debug.getregistry()) + replaced_obj = {} +end -- M.replace_all() + +return M diff --git a/framework/lualib/thirdparty/hotfix/internal/module_updater.lua b/framework/lualib/thirdparty/hotfix/internal/module_updater.lua new file mode 100644 index 0000000..9bcdf22 --- /dev/null +++ b/framework/lualib/thirdparty/hotfix/internal/module_updater.lua @@ -0,0 +1,168 @@ +--- Updater to update loaded module. +-- Updating a table is to update metatable and sub-table of the old table, +-- and to update functions of the old table, keeping value fields. +-- Updating a function is to copy upvalues of old function to new function. +-- Functions will be replaced later after updating. + +local M = {} + +local update_table +local update_func + +-- Updated signature set to prevent self-reference dead loop. +local updated_sig = {} + +-- Map old function to new functions. +local updated_func_map = {} + +-- Do not update and replace protected objects. +-- Set to hotfix.protected. +local protected = {} + +-- Set to hotfix.log_debug. +function M.log_debug(msg_str) +end + +-- Check if function or table has been updated. Return true if updated. +local function check_updated(new_obj, old_obj, name, deep) + local signature = string.format("new(%s) old(%s)", tostring(new_obj), tostring(old_obj)) + M.log_debug(string.format("%sUpdate %s: %s", deep, name, signature)) + + if new_obj == old_obj then + M.log_debug(deep .. " Same") + return true + end + if updated_sig[signature] then + M.log_debug(deep .. " Already updated") + return true + end + updated_sig[signature] = true + return false +end + +-- Update new function with upvalues of old function. +-- Parameter name and deep are only for log. +function update_func(new_func, old_func, name, deep) + assert("function" == type(new_func)) + assert("function" == type(old_func)) + if protected[old_func] then + return + end + if check_updated(new_func, old_func, name, deep) then + return + end + deep = deep .. " " + updated_func_map[old_func] = new_func + + -- Get upvalues of old function. + local old_upvalue_map = {} + for i = 1, math.huge do + local name, value = debug.getupvalue(old_func, i) + if not name then + break + end + old_upvalue_map[name] = value + end + + local function log_dbg(name, from, to) + M.log_debug(string.format("%ssetupvalue %s: (%s) -> (%s)", deep, name, tostring(from), tostring(to))) + end + + -- Update new upvalues with old. + for i = 1, math.huge do + local name, value = debug.getupvalue(new_func, i) + if not name then + break + end + local old_value = old_upvalue_map[name] + if old_value then + local type_old_value = type(old_value) + if type_old_value ~= type(value) then + debug.setupvalue(new_func, i, old_value) + log_dbg(name, value, old_value) + elseif type_old_value == "function" then + update_func(value, old_value, name, deep) + elseif type_old_value == "table" then + update_table(value, old_value, name, deep) + debug.setupvalue(new_func, i, old_value) + else + debug.setupvalue(new_func, i, old_value) + log_dbg(name, value, old_value) + end + end -- if old_value + end -- for i +end -- update_func() + +-- Compare 2 tables and update the old table. Keep the old data. +function update_table(new_table, old_table, name, deep) + assert("table" == type(new_table)) + assert("table" == type(old_table)) + if protected[old_table] then + return + end + if check_updated(new_table, old_table, name, deep) then + return + end + deep = deep .. " " + + -- Compare 2 tables, and update old table. + for key, value in pairs(new_table) do + local old_value = old_table[key] + local type_value = type(value) + if type_value ~= type(old_value) then + old_table[key] = value + M.log_debug( + string.format("%sUpdate field %s: (%s) -> (%s)", deep, key, tostring(old_value), tostring(value)) + ) + elseif type_value == "function" then + update_func(value, old_value, key, deep) + elseif type_value == "table" then + update_table(value, old_value, key, deep) + end + end -- for + + -- Update metatable. + local old_meta = debug.getmetatable(old_table) + local new_meta = debug.getmetatable(new_table) + if type(old_meta) == "table" and type(new_meta) == "table" then + update_table(new_meta, old_meta, name .. "'s Meta", deep) + end +end -- update_table() + +-- Update new loaded object with package.loaded[module_name]. +local function update_loaded_module2(module_name, new_obj) + assert(nil ~= new_obj) + assert("string" == type(module_name)) + local old_obj = package.loaded[module_name] + local new_type = type(new_obj) + local old_type = type(old_obj) + if new_type == old_type then + if "table" == new_type then + update_table(new_obj, old_obj, module_name, "") + return + end + if "function" == new_type then + update_func(new_obj, old_obj, module_name, "") + return + end + end -- if new_type == old_type + M.log_debug(string.format("Directly replace module: old(%s) -> new(%s)", tostring(old_obj), tostring(new_obj))) + package.loaded[module_name] = new_obj +end -- update_loaded_module2() + +-- Update new loaded object with package.loaded[module_name]. +-- Return an updated function map (updated_func_map). +-- new_module_obj is the newly loaded module object. +function M.update_loaded_module(module_name, protected_objects, new_module_obj) + assert(type(module_name) == "string") + assert(type(protected_objects) == "table") + + protected = protected_objects + updated_func_map = {} + updated_sig = {} + update_loaded_module2(module_name, new_module_obj) + updated_sig = {} + return updated_func_map +end -- update_loaded_module() + +return M diff --git a/framework/lualib/thirdparty/json.lua b/framework/lualib/thirdparty/json.lua new file mode 100644 index 0000000..a9c23fd --- /dev/null +++ b/framework/lualib/thirdparty/json.lua @@ -0,0 +1,82 @@ +local skynet = require "skynet" +local cjson = require("cjson") + +local pairs = pairs +local tostring = tostring +local type = type +local xpcall = xpcall +local string_format = string.format + +-- 设置 处理稀疏数组 +-- https://www.kyne.com.au/~mark/software/lua-cjson-manual.html#encode_sparse_array +cjson.encode_sparse_array(true, 1, 1) + +-- https://github.com/cloudwu/lua-cjson/pull/8 +cjson.encode_empty_table_as_array("on") + +-- https://github.com/cloudwu/lua-cjson/pull/10 +cjson.encode_number_precision(16) + +local json = {} + +function json.check(var, str) + str = str or "root" + for k, v in pairs(var or {}) do + local tstr = string_format("%s[%s]", str, tostring(k)) + if type(k) == "number" and k > 1 and not var[k - 1] then + skynet.error("json check", tstr, "is a unvalided number") + end + if type(v) == "table" then + json.check(v, tstr) + end + end +end + +function json.encode(var) + local function _logExce(errLog) + skynet.error(var, errLog) + json.check(var) + end + + local status, result = xpcall(cjson.encode, _logExce, var) + if status then + return result + end +end + +function json.decode(text) + if not text then + return + end + + local function _logExce(errLog) + skynet.error("json decode", text, errLog) + end + + local status, result = xpcall(cjson.decode, _logExce, text) + if status then + return result + end +end + +function json.test() + local var = { + [1] = {"ssss"}, + [2] = 2 ^ 53, + [1000] = { + [50] = "test", + [3000] = {"data"}, + }, + } + + local str = json.encode(var) + skynet.error("json test 1:%s", str) + + var = {} + str = json.encode(var) + skynet.error("json test 2:%s xxx", str) +end + +-- json.test() + +return json diff --git a/framework/lualib/thirdparty/lecs/component.lua b/framework/lualib/thirdparty/lecs/component.lua new file mode 100755 index 0000000..b6485b2 --- /dev/null +++ b/framework/lualib/thirdparty/lecs/component.lua @@ -0,0 +1,12 @@ +---@class Component +local Component = class("Component") + +function Component:ctor() + self.__isinst = true +end + +function Component:isInstComponent() + return self.__isinst +end + +return Component diff --git a/framework/lualib/thirdparty/lecs/entity.lua b/framework/lualib/thirdparty/lecs/entity.lua new file mode 100755 index 0000000..436f2c2 --- /dev/null +++ b/framework/lualib/thirdparty/lecs/entity.lua @@ -0,0 +1,87 @@ +-- local Component = require("lecs.component") + +---@class Entity +---@field public id number +---@field private _world World +---@field private _singletonName boolean +---@field private _comps table +local Entity = class("Entity") + +function Entity:ctor() + self.id = 0 + self._world = nil + self._singletonName = nil + self._comps = {} +end + +---@public +---@param comp Component +function Entity:AddComponent(comp) + assert(comp:isInstComponent(), comp) + + self[comp.__cname] = comp + self._comps[comp.__cname] = 1 + + self._world:AddEntity(self) +end + +---@public +---@param name component name +function Entity:GetComponent(name) + return self[name] +end + +---@public +---@param comp Component +function Entity:RemoveComponent(comp) + self[comp.__cname] = nil + self._comps[comp.__cname] = nil + + self._world:AddEntity(self) +end + +---@public +---@return boolean +function Entity:IsSingleton() + return self._singletonName ~= nil +end + +---@public +---@return World +function Entity:GetWorld() + return self._world +end + +--region Private + +---@private +---@param world World +---@param id number +---@param singletonName string +function Entity:awakeFromPool(world, id, singletonName) + self.id = id + self._world = world + self._singletonName = singletonName + + if self._singletonName then + self._world:AddSingletonEntity(self) + end +end + +---@private +function Entity:recycleToPool() + ---clear all components + for k, _ in pairs(self._comps) do + self[k] = nil + end + + self._world = nil + self._comps = {} + self._singletonName = nil + + self.id = 0 +end + +--endregion + +return Entity diff --git a/framework/lualib/thirdparty/lecs/filter.lua b/framework/lualib/thirdparty/lecs/filter.lua new file mode 100755 index 0000000..71d620d --- /dev/null +++ b/framework/lualib/thirdparty/lecs/filter.lua @@ -0,0 +1,65 @@ +local TinyECS = require("lecs.tiny_ecs") + +---@class Filter +local Filter = {} + +---@public +---@vararg Comp +---@return function +function Filter.RequireAll(...) + local f = {...} + for i = 1, #f do + f[i] = f[i].__cname + end + + return TinyECS.requireAll(table.unpack(f)) +end + +---@public +---@vararg Component +---@return function +function Filter.RequireAny(...) + local f = {...} + for i = 1, #f do + f[i] = f[i].__cname + end + return TinyECS.requireAny(table.unpack(f)) +end + +---@public +---@vararg Component +---@return function +function Filter.RejectAll(...) + local f = {...} + for i = 1, #f do + f[i] = f[i].__cname + end + return TinyECS.rejectAll(table.unpack(f)) +end + +---@public +---@vararg Component +---@return function +function Filter.RejectAny(...) + local f = {...} + for i = 1, #f do + f[i] = f[i].__cname + end + return TinyECS.rejectAny(table.unpack(f)) +end + +---@public +---@vararg function +---@return function +function Filter.And(...) + return TinyECS.requireAll(...) +end + +---@public +---@vararg function +---@return function +function Filter.Or(...) + return TinyECS.requireAny(...) +end + +return Filter diff --git a/framework/lualib/thirdparty/lecs/global.lua b/framework/lualib/thirdparty/lecs/global.lua new file mode 100755 index 0000000..5c47a4d --- /dev/null +++ b/framework/lualib/thirdparty/lecs/global.lua @@ -0,0 +1,29 @@ +local Pool = require("lecs.pool") +local Entity = require("lecs.entity") + +---@class Global +---@field private _entityPool Pool +local Global = {} + +function Global:init() + self._entityPool = Pool({ + ctor = { + [Pool.DEFAULT_TAG] = Entity.new -- obj create function + } + }) +end + +---@param world World +---@return Entity +function Global:GetEntity(world, eid, singletonName) + return self._entityPool:get(world, eid, singletonName) +end + +---@param entity Entity +function Global:RecycleEntity(entity) + self._entityPool:recycle(entity) +end + +Global:init() + +return Global \ No newline at end of file diff --git a/framework/lualib/thirdparty/lecs/init.lua b/framework/lualib/thirdparty/lecs/init.lua new file mode 100755 index 0000000..5dee961 --- /dev/null +++ b/framework/lualib/thirdparty/lecs/init.lua @@ -0,0 +1,8 @@ +-- https://github.com/RayStudio36/ray-ecs.lua + +return { + Component = require("lecs.component"), + Entity = require("lecs.entity"), + System = require("lecs.system"), + World = require("lecs.world") +} diff --git a/framework/lualib/thirdparty/lecs/pool.lua b/framework/lualib/thirdparty/lecs/pool.lua new file mode 100755 index 0000000..44afb07 --- /dev/null +++ b/framework/lualib/thirdparty/lecs/pool.lua @@ -0,0 +1,234 @@ +local setmetatable = setmetatable +local pairs = pairs + +--- +---Table pool for lua +--- +---@class Pool +---@field public DEFAULT_TAG string @default object tag for pool +---@field private _ctorHandle table +---@field private _pools table +local Pool = {} + +local DEFAULT_TAG = "__" + +Pool.DEFAULT_TAG = DEFAULT_TAG +Pool.__index = Pool + +setmetatable( + Pool, + { + __call = function(class, opt) + local instance = {} + setmetatable(instance, Pool) + instance:new(opt) + return instance + end + } +) + +---@param pool Pool +---@param tag string +---@param create boolean +local function getPoolArr(pool, tag, create) + local poolArr = pool._pools[tag] + if not poolArr and create then + poolArr = {} + pool._pools[tag] = poolArr + end + return poolArr +end + +---@param pool Pool +---@param tag string +local function createInstance(pool, tag) + tag = tag or DEFAULT_TAG + local ctorHandle = pool._ctorHandle[tag] + if ctorHandle then + return ctorHandle() + else + return {} + end +end + +---@param pool Pool +---@param tag string +---@vararg any +local function getFromPool(pool, tag, ...) + local ins = nil + if pool._pools[tag] then + local poolArr = pool._pools[tag] + if #poolArr > 0 then + ins = poolArr[#poolArr] + poolArr[#poolArr] = nil + end + end + + if not ins then + ins = createInstance(pool, tag) + end + + if ins.awakeFromPool then + ins:awakeFromPool(...) + end + + return ins +end + +---@param pool Pool +---@param tag string +---@param t string +---@vararg any +local function recycleToPool(pool, tag, t, ...) + local poolArr = getPoolArr(pool, tag, true) + + poolArr[#poolArr + 1] = t + if t.recycleToPool then + t:recycleToPool(...) + end +end + +---@param pool Pool +---@param tag string +---@param clearCtorHandle boolean +local function clearPool(pool, tag, clearCtorHandle) + pool._pools[tag] = nil + if clearCtorHandle then + pool._ctorHandle[tag] = nil + end +end + +---@private +function Pool:new(opt) + self._ctorHandle = {} + self._pools = {} + + if opt then + if opt.ctor then + for k, v in pairs(opt.ctor) do + self._ctorHandle[k] = v + end + end + + if opt.presize then + for k, v in pairs(opt.presize) do + self:presize(v, k) + end + end + end +end + +--- +---Set table constructor handle for object +---when get a table from pool, if there isn't a table in pool, +---will use this constructor handle to create new one and return it. +---@public +---@param handle function @create object handle +---@param tag string @[option] object tag +function Pool:setCtorHandle(handle, tag) + tag = tag or DEFAULT_TAG + self._ctorHandle[tag] = handle +end + +--- +---Get table from pool +---if table has a awakeFromPool function, +---will call this function with parameters by pass. +---@public +---@vararg any +---@return table +function Pool:get(...) + return getFromPool(self, DEFAULT_TAG, ...) +end + +--- +---Get table from pool with object tag +---if table has a awakeFromPool function, +---will call taht function with parameters by pass. +---@public +---@param tag string +---@vararg any +---@return table +function Pool:getWithTag(tag, ...) + return getFromPool(self, tag, ...) +end + +--- +---Recycle table to pool +---if table has a recycleToPool function, +---will call that function with parameters by pass. +---@public +---@param t table +---@vararg any +function Pool:recycle(t, ...) + recycleToPool(self, DEFAULT_TAG, t, ...) +end + +--- +---Recycle table to pool with object tag +---if table has a recycleToPool function, +---will call that function with parameters by pass. +---@public +---@param t table +---@param tag string +---@vararg any +function Pool:recycleWithTag(t, tag, ...) + recycleToPool(self, tag, t, ...) +end + +--- +---Pre create some table with an option object tag. +---@public +---@param count number +---@param tag string @[option] object tag +---@return boolean +function Pool:presize(count, tag) + local poolArr = getPoolArr(self, tag or DEFAULT_TAG, true) + if #poolArr >= count then + return false + end + + local needCreateSize = count - #poolArr + local ctorHandle = self._ctorHandle[tag] + local createFunction = ctorHandle and function() + return ctorHandle() + end or function() + return {} + end + + for _ = 1, needCreateSize do + poolArr[#poolArr + 1] = createFunction() + end + + return true +end + +--- +---Clear objects in pool +---@public +---@param clearCtorHandle boolean @default false +function Pool:clear(clearCtorHandle) + clearCtorHandle = clearCtorHandle or false + clearPool(self, DEFAULT_TAG, clearCtorHandle) +end + +--- +---Clear all objects in pool +---@public +---@param clearCtorHandle boolean @default false +function Pool:clearAll(_) + self._pools = {} + self._ctorHandle = {} +end + +--- +---Clear all objects with an object tag in pool +---@public +---@param tag string +---@param clearCtorHandle boolean @default false +function Pool:clearTag(tag, clearCtorHandle) + clearCtorHandle = clearCtorHandle or false + clearPool(self, tag, clearCtorHandle) +end + +return Pool diff --git a/framework/lualib/thirdparty/lecs/system.lua b/framework/lualib/thirdparty/lecs/system.lua new file mode 100755 index 0000000..df2acf1 --- /dev/null +++ b/framework/lualib/thirdparty/lecs/system.lua @@ -0,0 +1,37 @@ +local TinyECS = require("lecs.tiny_ecs") +local Filter = require("lecs.filter") + +---@class System +---@field protected filter Filter +---@field private _system table +---@field private _world World +local System = class("System") + +function System:ctor() + self.filter = Filter + + local system = TinyECS.system() + system.filter = self:CreateFilter() + system.update = function(t, dt) + self:Update(t.entities, dt) + end + + self._system = system + self._world = nil +end + +function System:CreateFilter() +end + +---@param entities Entity[] +---@param dt number +function System:Update(_, _) +end + +---@protected +---@return World +function System:GetWorld() + return self._world +end + +return System diff --git a/framework/lualib/thirdparty/lecs/test.lua b/framework/lualib/thirdparty/lecs/test.lua new file mode 100755 index 0000000..2343541 --- /dev/null +++ b/framework/lualib/thirdparty/lecs/test.lua @@ -0,0 +1,44 @@ +local lecs = require("lecs.init") +local Component = lecs.Component +local System = lecs.System +local World = lecs.World + +local PlayerComponent = class("PlayerComponent", Component) +function PlayerComponent:ctor(...) + PlayerComponent.super.ctor(self, ...) + + self.name = "Joe" + self.phrase = "I'm a plumber." + self.mass = 150 + self.hairColor = "brown" +end + +local TalkingSystem = class("TalkingSystem", System) +function TalkingSystem:CreateFilter() + return self.filter.RequireAll(PlayerComponent) +end + +function TalkingSystem:Update(entities, dt) + for _, e in ipairs(entities) do + local PlayerComp = e:GetComponent("PlayerComponent") + PlayerComp.mass = PlayerComp.mass + dt * 3 + print(("%s who weighs %d pounds, says %q."):format(PlayerComp.name, PlayerComp.mass, PlayerComp.phrase)) + end +end + +local world = World.new() +local talk_system = TalkingSystem.new() +world:AddSystem(talk_system) + +local playerEntity = world:CreateEntity() +playerEntity:AddComponent(PlayerComponent.new()) + +return function() + for _ = 1, 20 do + world:Update(1) + end +end + +-- skynet.timeout(20 * 100, function () +-- require("fecs.test")() +-- end) diff --git a/framework/lualib/thirdparty/lecs/tiny_ecs.lua b/framework/lualib/thirdparty/lecs/tiny_ecs.lua new file mode 100755 index 0000000..8f0b26d --- /dev/null +++ b/framework/lualib/thirdparty/lecs/tiny_ecs.lua @@ -0,0 +1,864 @@ +--[[ +Copyright (c) 2016 Calvin Rose + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] +--- @module tiny-ecs +-- @https://github.com/bakpakin/tiny-ecs +-- @author Calvin Rose +-- @license MIT +-- @copyright 2016 +local tiny = {} + +-- Local versions of standard lua functions +local tinsert = table.insert +local tremove = table.remove +local tsort = table.sort +local setmetatable = setmetatable +local type = type +local select = select + +-- Local versions of the library functions +local tiny_manageEntities +local tiny_manageSystems +local tiny_addEntity +local tiny_addSystem +local tiny_add +local tiny_removeEntity +local tiny_removeSystem + +--- Filter functions. +-- A Filter is a function that selects which Entities apply to a System. +-- Filters take two parameters, the System and the Entity, and return a boolean +-- value indicating if the Entity should be processed by the System. A truthy +-- value includes the entity, while a falsey (nil or false) value excludes the +-- entity. +-- +-- Filters must be added to Systems by setting the `filter` field of the System. +-- Filter's returned by tiny-ecs's Filter functions are immutable and can be +-- used by multiple Systems. +-- +-- local f1 = tiny.requireAll("position", "velocity", "size") +-- local f2 = tiny.requireAny("position", "velocity", "size") +-- +-- local e1 = { +-- position = {2, 3}, +-- velocity = {3, 3}, +-- size = {4, 4} +-- } +-- +-- local entity2 = { +-- position = {4, 5}, +-- size = {4, 4} +-- } +-- +-- local e3 = { +-- position = {2, 3}, +-- velocity = {3, 3} +-- } +-- +-- print(f1(nil, e1), f1(nil, e2), f1(nil, e3)) -- prints true, false, false +-- print(f2(nil, e1), f2(nil, e2), f2(nil, e3)) -- prints true, true, true +-- +-- Filters can also be passed as arguments to other Filter constructors. This is +-- a powerful way to create complex, custom Filters that select a very specific +-- set of Entities. +-- +-- -- Selects Entities with an "image" Component, but not Entities with a +-- -- "Player" or "Enemy" Component. +-- filter = tiny.requireAll("image", tiny.rejectAny("Player", "Enemy")) +-- +-- @section Filter + +-- A helper function to compile filters. +local filterJoin + +-- A helper function to filters from string +local filterBuildString + +do + local loadstring = load + local function getchr(c) + return "\\" .. c:byte() + end + local function make_safe(text) + return ("%q"):format(text):gsub("\n", "n"):gsub("[\128-\255]", getchr) + end + + local function filterJoinRaw(prefix, seperator, ...) + local accum = {} + local build = {} + for i = 1, select("#", ...) do + local item = select(i, ...) + if type(item) == "string" then + accum[#accum + 1] = ("(e[%s] ~= nil)"):format(make_safe(item)) + elseif type(item) == "function" then + build[#build + 1] = ("local subfilter_%d_ = select(%d, ...)"):format(i, i) + accum[#accum + 1] = ("(subfilter_%d_(system, e))"):format(i) + else + error "Filter token must be a string or a filter function." + end + end + local source = + ("%s\nreturn function(system, e) return %s(%s) end"):format( + table.concat(build, "\n"), + prefix, + table.concat(accum, seperator) + ) + local loader, err = loadstring(source) + if err then + error(err) + end + return loader(...) + end + + function filterJoin(...) + local state, value = pcall(filterJoinRaw, ...) + if state then + return value + else + return nil, value + end + end + + local function buildPart(str) + local accum = {} + local subParts = {} + str = + str:gsub( + "%b()", + function(p) + subParts[#subParts + 1] = buildPart(p:sub(2, -2)) + return ("\255%d"):format(#subParts) + end + ) + for invert, part, sep in str:gmatch("(%!?)([^%|%&%!]+)([%|%&]?)") do + if part:match("^\255%d+$") then + local partIndex = tonumber(part:match(part:sub(2))) + accum[#accum + 1] = ("%s(%s)"):format(invert == "" and "" or "not", subParts[partIndex]) + else + accum[#accum + 1] = ("(e[%s] %s nil)"):format(make_safe(part), invert == "" and "~=" or "==") + end + if sep ~= "" then + accum[#accum + 1] = (sep == "|" and " or " or " and ") + end + end + return table.concat(accum) + end + + function filterBuildString(str) + local source = ("return function(_, e) return %s end"):format(buildPart(str)) + local loader, err = loadstring(source) + if err then + error(err) + end + return loader() + end +end + +--- Makes a Filter that selects Entities with all specified Components and +-- Filters. +function tiny.requireAll(...) + return filterJoin("", " and ", ...) +end + +--- Makes a Filter that selects Entities with at least one of the specified +-- Components and Filters. +function tiny.requireAny(...) + return filterJoin("", " or ", ...) +end + +--- Makes a Filter that rejects Entities with all specified Components and +-- Filters, and selects all other Entities. +function tiny.rejectAll(...) + return filterJoin("not", " and ", ...) +end + +--- Makes a Filter that rejects Entities with at least one of the specified +-- Components and Filters, and selects all other Entities. +function tiny.rejectAny(...) + return filterJoin("not", " or ", ...) +end + +--- Makes a Filter from a string. Syntax of `pattern` is as follows. +-- +-- * Tokens are alphanumeric strings including underscores. +-- * Tokens can be separated by |, &, or surrounded by parentheses. +-- * Tokens can be prefixed with !, and are then inverted. +-- +-- Examples are best: +-- 'a|b|c' - Matches entities with an 'a' OR 'b' OR 'c'. +-- 'a&!b&c' - Matches entities with an 'a' AND NOT 'b' AND 'c'. +-- 'a|(b&c&d)|e - Matches 'a' OR ('b' AND 'c' AND 'd') OR 'e' +-- @param pattern +function tiny.filter(pattern) + local state, value = pcall(filterBuildString, pattern) + if state then + return value + else + return nil, value + end +end + +--- System functions. +-- A System is a wrapper around function callbacks for manipulating Entities. +-- Systems are implemented as tables that contain at least one method; +-- an update function that takes parameters like so: +-- +-- * `function system:update(dt)`. +-- +-- There are also a few other optional callbacks: +-- +-- * `function system:filter(entity)` - Returns true if this System should +-- include this Entity, otherwise should return false. If this isn't specified, +-- no Entities are included in the System. +-- * `function system:onAdd(entity)` - Called when an Entity is added to the +-- System. +-- * `function system:onRemove(entity)` - Called when an Entity is removed +-- from the System. +-- * `function system:onModify(dt)` - Called when the System is modified by +-- adding or removing Entities from the System. +-- * `function system:onAddToWorld(world)` - Called when the System is added +-- to the World, before any entities are added to the system. +-- * `function system:onRemoveFromWorld(world)` - Called when the System is +-- removed from the world, after all Entities are removed from the System. +-- * `function system:preWrap(dt)` - Called on each system before update is +-- called on any system. +-- * `function system:postWrap(dt)` - Called on each system in reverse order +-- after update is called on each system. The idea behind `preWrap` and +-- `postWrap` is to allow for systems that modify the behavior of other systems. +-- Say there is a DrawingSystem, which draws sprites to the screen, and a +-- PostProcessingSystem, that adds some blur and bloom effects. In the preWrap +-- method of the PostProcessingSystem, the System could set the drawing target +-- for the DrawingSystem to a special buffer instead the screen. In the postWrap +-- method, the PostProcessingSystem could then modify the buffer and render it +-- to the screen. In this setup, the PostProcessingSystem would be added to the +-- World after the drawingSystem (A similar but less flexible behavior could +-- be accomplished with a single custom update function in the DrawingSystem). +-- +-- For Filters, it is convenient to use `tiny.requireAll` or `tiny.requireAny`, +-- but one can write their own filters as well. Set the Filter of a System like +-- so: +-- system.filter = tiny.requireAll("a", "b", "c") +-- or +-- function system:filter(entity) +-- return entity.myRequiredComponentName ~= nil +-- end +-- +-- All Systems also have a few important fields that are initialized when the +-- system is added to the World. A few are important, and few should be less +-- commonly used. +-- +-- * The `world` field points to the World that the System belongs to. Useful +-- for adding and removing Entities from the world dynamically via the System. +-- * The `active` flag is whether or not the System is updated automatically. +-- Inactive Systems should be updated manually or not at all via +-- `system:update(dt)`. Defaults to true. +-- * The `entities` field is an ordered list of Entities in the System. This +-- list can be used to quickly iterate through all Entities in a System. +-- * The `interval` field is an optional field that makes Systems update at +-- certain intervals using buffered time, regardless of World update frequency. +-- For example, to make a System update once a second, set the System's interval +-- to 1. +-- * The `index` field is the System's index in the World. Lower indexed +-- Systems are processed before higher indices. The `index` is a read only +-- field; to set the `index`, use `tiny.setSystemIndex(world, system)`. +-- * The `indices` field is a table of Entity keys to their indices in the +-- `entities` list. Most Systems can ignore this. +-- * The `modified` flag is an indicator if the System has been modified in +-- the last update. If so, the `onModify` callback will be called on the System +-- in the next update, if it has one. This is usually managed by tiny-ecs, so +-- users should mostly ignore this, too. +-- +-- There is another option to (hopefully) increase performance in systems that +-- have items added to or removed from them often, and have lots of entities in +-- them. Setting the `nocache` field of the system might improve performance. +-- It is still experimental. There are some restriction to systems without +-- caching, however. +-- +-- * There is no `entities` table. +-- * Callbacks such onAdd, onRemove, and onModify will never be called +-- * Noncached systems cannot be sorted (There is no entities list to sort). +-- +-- @section System + +-- Use an empty table as a key for identifying Systems. Any table that contains +-- this key is considered a System rather than an Entity. +local systemTableKey = {"SYSTEM_TABLE_KEY"} + +-- Checks if a table is a System. +local function isSystem(table) + return table[systemTableKey] +end + +-- Update function for all Processing Systems. +local function processingSystemUpdate(system, dt) + local preProcess = system.preProcess + local process = system.process + local postProcess = system.postProcess + + if preProcess then + preProcess(system, dt) + end + + if process then + if system.nocache then + local entities = system.world.entities + local filter = system.filter + if filter then + for i = 1, #entities do + local entity = entities[i] + if filter(system, entity) then + process(system, entity, dt) + end + end + end + else + local entities = system.entities + for i = 1, #entities do + process(system, entities[i], dt) + end + end + end + + if postProcess then + postProcess(system, dt) + end +end + +-- Sorts Systems by a function system.sortDelegate(entity1, entity2) on modify. +local function sortedSystemOnModify(system) + local entities = system.entities + local indices = system.indices + local sortDelegate = system.sortDelegate + if not sortDelegate then + local compare = system.compare + sortDelegate = function(e1, e2) + return compare(system, e1, e2) + end + system.sortDelegate = sortDelegate + end + tsort(entities, sortDelegate) + for i = 1, #entities do + indices[entities[i]] = i + end +end + +--- Creates a new System or System class from the supplied table. If `table` is +-- nil, creates a new table. +function tiny.system(table) + table = table or {} + table[systemTableKey] = true + return table +end + +--- Creates a new Processing System or Processing System class. Processing +-- Systems process each entity individual, and are usually what is needed. +-- Processing Systems have three extra callbacks besides those inheritted from +-- vanilla Systems. +-- +-- function system:preProcess(dt) -- Called before iteration. +-- function system:process(entity, dt) -- Process each entity. +-- function system:postProcess(dt) -- Called after iteration. +-- +-- Processing Systems have their own `update` method, so don't implement a +-- a custom `update` callback for Processing Systems. +-- @see system +function tiny.processingSystem(table) + table = table or {} + table[systemTableKey] = true + table.update = processingSystemUpdate + return table +end + +--- Creates a new Sorted System or Sorted System class. Sorted Systems sort +-- their Entities according to a user-defined method, `system:compare(e1, e2)`, +-- which should return true if `e1` should come before `e2` and false otherwise. +-- Sorted Systems also override the default System's `onModify` callback, so be +-- careful if defining a custom callback. However, for processing the sorted +-- entities, consider `tiny.sortedProcessingSystem(table)`. +-- @see system +function tiny.sortedSystem(table) + table = table or {} + table[systemTableKey] = true + table.onModify = sortedSystemOnModify + return table +end + +--- Creates a new Sorted Processing System or Sorted Processing System class. +-- Sorted Processing Systems have both the aspects of Processing Systems and +-- Sorted Systems. +-- @see system +-- @see processingSystem +-- @see sortedSystem +function tiny.sortedProcessingSystem(table) + table = table or {} + table[systemTableKey] = true + table.update = processingSystemUpdate + table.onModify = sortedSystemOnModify + return table +end + +--- World functions. +-- A World is a container that manages Entities and Systems. Typically, a +-- program uses one World at a time. +-- +-- For all World functions except `tiny.world(...)`, object-oriented syntax can +-- be used instead of the documented syntax. For example, +-- `tiny.add(world, e1, e2, e3)` is the same as `world:add(e1, e2, e3)`. +-- @section World + +-- Forward declaration +local worldMetaTable + +--- Creates a new World. +-- Can optionally add default Systems and Entities. Returns the new World along +-- with default Entities and Systems. +function tiny.world(...) + local ret = + setmetatable( + { + -- List of Entities to remove + entitiesToRemove = {}, + -- List of Entities to change + entitiesToChange = {}, + -- List of Entities to add + systemsToAdd = {}, + -- List of Entities to remove + systemsToRemove = {}, + -- Set of Entities + entities = {}, + -- List of Systems + systems = {} + }, + worldMetaTable + ) + + tiny_add(ret, ...) + tiny_manageSystems(ret) + tiny_manageEntities(ret) + + return ret, ... +end + +--- Adds an Entity to the world. +-- Also call this on Entities that have changed Components such that they +-- match different Filters. Returns the Entity. +function tiny.addEntity(world, entity) + local e2c = world.entitiesToChange + e2c[#e2c + 1] = entity + return entity +end +tiny_addEntity = tiny.addEntity + +--- Adds a System to the world. Returns the System. +function tiny.addSystem(world, system) + assert(system.world == nil, "System already belongs to a World.") + local s2a = world.systemsToAdd + s2a[#s2a + 1] = system + system.world = world + return system +end +tiny_addSystem = tiny.addSystem + +--- Shortcut for adding multiple Entities and Systems to the World. Returns all +-- added Entities and Systems. +function tiny.add(world, ...) + for i = 1, select("#", ...) do + local obj = select(i, ...) + if obj then + if isSystem(obj) then + tiny_addSystem(world, obj) + else -- Assume obj is an Entity + tiny_addEntity(world, obj) + end + end + end + return ... +end +tiny_add = tiny.add + +--- Removes an Entity from the World. Returns the Entity. +function tiny.removeEntity(world, entity) + local e2r = world.entitiesToRemove + e2r[#e2r + 1] = entity + return entity +end +tiny_removeEntity = tiny.removeEntity + +--- Removes a System from the world. Returns the System. +function tiny.removeSystem(world, system) + assert(system.world == world, "System does not belong to this World.") + local s2r = world.systemsToRemove + s2r[#s2r + 1] = system + return system +end +tiny_removeSystem = tiny.removeSystem + +--- Shortcut for removing multiple Entities and Systems from the World. Returns +-- all removed Systems and Entities +function tiny.remove(world, ...) + for i = 1, select("#", ...) do + local obj = select(i, ...) + if obj then + if isSystem(obj) then + tiny_removeSystem(world, obj) + else -- Assume obj is an Entity + tiny_removeEntity(world, obj) + end + end + end + return ... +end + +-- Adds and removes Systems that have been marked from the World. +function tiny_manageSystems(world) + local s2a, s2r = world.systemsToAdd, world.systemsToRemove + + -- Early exit + if #s2a == 0 and #s2r == 0 then + return + end + + world.systemsToAdd = {} + world.systemsToRemove = {} + + local worldEntityList = world.entities + local systems = world.systems + + -- Remove Systems + for i = 1, #s2r do + local system = s2r[i] + local index = system.index + local onRemove = system.onRemove + if onRemove and not system.nocache then + local entityList = system.entities + for j = 1, #entityList do + onRemove(system, entityList[j]) + end + end + tremove(systems, index) + for j = index, #systems do + systems[j].index = j + end + local onRemoveFromWorld = system.onRemoveFromWorld + if onRemoveFromWorld then + onRemoveFromWorld(system, world) + end + s2r[i] = nil + + -- Clean up System + system.world = nil + system.entities = nil + system.indices = nil + system.index = nil + end + + -- Add Systems + for i = 1, #s2a do + local system = s2a[i] + if systems[system.index or 0] ~= system then + if not system.nocache then + system.entities = {} + system.indices = {} + end + if system.active == nil then + system.active = true + end + system.modified = true + system.world = world + local index = #systems + 1 + system.index = index + systems[index] = system + local onAddToWorld = system.onAddToWorld + if onAddToWorld then + onAddToWorld(system, world) + end + + -- Try to add Entities + if not system.nocache then + local entityList = system.entities + local entityIndices = system.indices + local onAdd = system.onAdd + local filter = system.filter + if filter then + for j = 1, #worldEntityList do + local entity = worldEntityList[j] + if filter(system, entity) then + local entityIndex = #entityList + 1 + entityList[entityIndex] = entity + entityIndices[entity] = entityIndex + if onAdd then + onAdd(system, entity) + end + end + end + end + end + end + s2a[i] = nil + end +end + +-- Adds, removes, and changes Entities that have been marked. +function tiny_manageEntities(world) + local e2r = world.entitiesToRemove + local e2c = world.entitiesToChange + + -- Early exit + if #e2r == 0 and #e2c == 0 then + return + end + + world.entitiesToChange = {} + world.entitiesToRemove = {} + + local entities = world.entities + local systems = world.systems + + -- Change Entities + for i = 1, #e2c do + local entity = e2c[i] + -- Add if needed + if not entities[entity] then + local index = #entities + 1 + entities[entity] = index + entities[index] = entity + end + for j = 1, #systems do + local system = systems[j] + if not system.nocache then + local ses = system.entities + local seis = system.indices + local index = seis[entity] + local filter = system.filter + if filter and filter(system, entity) then + if not index then + system.modified = true + index = #ses + 1 + ses[index] = entity + seis[entity] = index + local onAdd = system.onAdd + if onAdd then + onAdd(system, entity) + end + end + elseif index then + system.modified = true + local tmpEntity = ses[#ses] + ses[index] = tmpEntity + seis[tmpEntity] = index + seis[entity] = nil + ses[#ses] = nil + local onRemove = system.onRemove + if onRemove then + onRemove(system, entity) + end + end + end + end + e2c[i] = nil + end + + -- Remove Entities + for i = 1, #e2r do + local entity = e2r[i] + e2r[i] = nil + local listIndex = entities[entity] + if listIndex then + -- Remove Entity from world state + local lastEntity = entities[#entities] + entities[lastEntity] = listIndex + entities[entity] = nil + entities[listIndex] = lastEntity + entities[#entities] = nil + -- Remove from cached systems + for j = 1, #systems do + local system = systems[j] + if not system.nocache then + local ses = system.entities + local seis = system.indices + local index = seis[entity] + if index then + system.modified = true + local tmpEntity = ses[#ses] + ses[index] = tmpEntity + seis[tmpEntity] = index + seis[entity] = nil + ses[#ses] = nil + local onRemove = system.onRemove + if onRemove then + onRemove(system, entity) + end + end + end + end + end + end +end + +--- Manages Entities and Systems marked for deletion or addition. Call this +-- before modifying Systems and Entities outside of a call to `tiny.update`. +-- Do not call this within a call to `tiny.update`. +function tiny.refresh(world) + tiny_manageSystems(world) + tiny_manageEntities(world) + local systems = world.systems + for i = #systems, 1, -1 do + local system = systems[i] + if system.active then + local onModify = system.onModify + if onModify and system.modified then + onModify(system, 0) + end + system.modified = false + end + end +end + +--- Updates the World by dt (delta time). Takes an optional parameter, `filter`, +-- which is a Filter that selects Systems from the World, and updates only those +-- Systems. If `filter` is not supplied, all Systems are updated. Put this +-- function in your main loop. +function tiny.update(world, dt, filter) + tiny_manageSystems(world) + tiny_manageEntities(world) + + local systems = world.systems + + -- Iterate through Systems IN REVERSE ORDER + for i = #systems, 1, -1 do + local system = systems[i] + if system.active then + -- Call the modify callback on Systems that have been modified. + local onModify = system.onModify + if onModify and system.modified then + onModify(system, dt) + end + local preWrap = system.preWrap + if preWrap and ((not filter) or filter(world, system)) then + preWrap(system, dt) + end + end + end + + -- Iterate through Systems IN ORDER + for i = 1, #systems do + local system = systems[i] + if system.active and ((not filter) or filter(world, system)) then + -- Update Systems that have an update method (most Systems) + local update = system.update + if update then + local interval = system.interval + if interval then + local bufferedTime = (system.bufferedTime or 0) + dt + while bufferedTime >= interval do + bufferedTime = bufferedTime - interval + update(system, interval) + end + system.bufferedTime = bufferedTime + else + update(system, dt) + end + end + + system.modified = false + end + end + + -- Iterate through Systems IN ORDER AGAIN + for i = 1, #systems do + local system = systems[i] + local postWrap = system.postWrap + if postWrap and system.active and ((not filter) or filter(world, system)) then + postWrap(system, dt) + end + end +end + +--- Removes all Entities from the World. +function tiny.clearEntities(world) + local el = world.entities + for i = 1, #el do + tiny_removeEntity(world, el[i]) + end +end + +--- Removes all Systems from the World. +function tiny.clearSystems(world) + local systems = world.systems + for i = #systems, 1, -1 do + tiny_removeSystem(world, systems[i]) + end +end + +--- Gets number of Entities in the World. +function tiny.getEntityCount(world) + return #world.entities +end + +--- Gets number of Systems in World. +function tiny.getSystemCount(world) + return #world.systems +end + +--- Sets the index of a System in the World, and returns the old index. Changes +-- the order in which they Systems processed, because lower indexed Systems are +-- processed first. Returns the old system.index. +function tiny.setSystemIndex(world, system, index) + tiny_manageSystems(world) + local oldIndex = system.index + local systems = world.systems + + if index < 0 then + index = tiny.getSystemCount(world) + 1 + index + end + + tremove(systems, oldIndex) + tinsert(systems, index, system) + + for i = oldIndex, index, index >= oldIndex and 1 or -1 do + systems[i].index = i + end + + return oldIndex +end + +-- Construct world metatable. +worldMetaTable = { + __index = { + add = tiny.add, + addEntity = tiny.addEntity, + addSystem = tiny.addSystem, + remove = tiny.remove, + removeEntity = tiny.removeEntity, + removeSystem = tiny.removeSystem, + refresh = tiny.refresh, + update = tiny.update, + clearEntities = tiny.clearEntities, + clearSystems = tiny.clearSystems, + getEntityCount = tiny.getEntityCount, + getSystemCount = tiny.getSystemCount, + setSystemIndex = tiny.setSystemIndex + }, + __tostring = function() + return "" + end +} + +return tiny diff --git a/framework/lualib/thirdparty/lecs/world.lua b/framework/lualib/thirdparty/lecs/world.lua new file mode 100755 index 0000000..52dd960 --- /dev/null +++ b/framework/lualib/thirdparty/lecs/world.lua @@ -0,0 +1,137 @@ +local Global = require("lecs.global") +local TinyECS = require("lecs.tiny_ecs") + +---@class World +---@field private _world table +---@field private _eid number +---@field private _singletonEntity table +---@field private _changedEntityCache Entity +local World = class("World") + +function World:ctor() + self._world = TinyECS.world() + self._eid = 0 + self._singletonEntity = {} + + self._changedEntityCache = nil +end + +-- region Public + +---@public +---@param dt number +function World:Update(dt) + if self._changedEntityCache then + self._changedEntityCache = nil + end + + self._world:update(dt) +end + +---@public +---@param system System +function World:AddSystem(system) + self._world:addSystem(system._system) + system._world = self +end + +---@public +---@param system System +function World:RemoveSystem(system) + self._world:removeSystem(system._system) + system._world = nil +end + +---@public +---@return Entity +function World:CreateEntity() + local e = Global:GetEntity(self, self:GetEid()) + self:AddEntity(e) + return e +end + +---@public +---@param name string SingletonEntity Identifier +---@return Entity +function World:CreateSingletonEntity(name) + if self._singletonEntity[name] then + return self._singletonEntity[name] + end + + local e = Global:GetEntity(self, self:GetEid(), name) + self:AddEntity(e) + return e +end + +---@public +---@param entity Entity +function World:RemoveEntity(entity) + if entity._singletonName then + self:removeSingletonEntity(entity) + end + + self._world:removeEntity(entity) + + if self._changedEntityCache == entity then + self._changedEntityCache = nil + end + + Global:RecycleEntity(entity) +end + +---@public +---@param singletonName string +---@return Entity +function World:GetSingletonEntity(singletonName) + return self._singletonEntity[singletonName] +end + +-- endregion + +-- region Private + +---@private +function World:GetEid() + self._eid = self._eid + 1 + return self._eid +end + +---@private +---@param entity Entity +function World:AddEntity(entity) + if entity ~= self._changedEntityCache then + self._changedEntityCache = entity + self._world:addEntity(entity) + end +end + +---@private +---@param entity Entity +function World:AddSingletonEntity(entity) + if self._singletonEntity[entity._singletonName] then + print(string.format("Singleton entity %s already in this world"), entity._singletonName) + return + end + + self._singletonEntity[entity._singletonName] = entity +end + +---@private +---@param entity Entity | string +function World:RemoveSingletonEntity(entity) + local singletonName = entity + if not type(entity) == "string" then + singletonName = entity._singletonName + end + + if not self._singletonEntity[singletonName] then + print(string.format("Entity %s isn't a singleton"), singletonName) + return + end + + self._singletonEntity[singletonName] = nil +end + +-- endregion + +return World diff --git a/framework/lualib/thirdparty/lru/lru.lua b/framework/lualib/thirdparty/lru/lru.lua new file mode 100644 index 0000000..ade67c8 --- /dev/null +++ b/framework/lualib/thirdparty/lru/lru.lua @@ -0,0 +1,159 @@ +-- lua-LRU, LRU cache in Lua +-- Copyright (c) 2015 Boris Nagaev +-- See the LICENSE file for terms of use. +-- https://github.com/starius/lua-lru +local setmetatable = setmetatable +local assert = assert + +local LRU = {} + +function LRU.new(max_size, max_bytes) + + assert(max_size >= 1, "max_size must be >= 1") + assert(not max_bytes or max_bytes >= 1, "max_bytes must be >= 1") + + -- current size + local size = 0 + local bytes_used = 0 + + -- map is a hash map from keys to tuples + -- tuple: value, prev, next, key + -- prev and next are pointers to tuples + local map = {} + + -- indices of tuple + local VALUE = 1 + local PREV = 2 + local NEXT = 3 + local KEY = 4 + local BYTES = 5 + + -- newest and oldest are ends of double-linked list + local newest = nil -- first + local oldest = nil -- last + + local removed_tuple + -- remove a tuple from linked list + local function cut(tuple) + local tuple_prev = tuple[PREV] + local tuple_next = tuple[NEXT] + tuple[PREV] = nil + tuple[NEXT] = nil + if tuple_prev and tuple_next then + tuple_prev[NEXT] = tuple_next + tuple_next[PREV] = tuple_prev + elseif tuple_prev then + -- tuple is the oldest element + tuple_prev[NEXT] = nil + oldest = tuple_prev + elseif tuple_next then + -- tuple is the newest element + tuple_next[PREV] = nil + newest = tuple_next + else + -- tuple is the only element + newest = nil + oldest = nil + end + end + + -- insert a tuple to the newest end + local function setNewest(tuple) + if not newest then + newest = tuple + oldest = tuple + else + tuple[NEXT] = newest + newest[PREV] = tuple + newest = tuple + end + end + + local function del(key, tuple) + map[key] = nil + cut(tuple) + size = size - 1 + bytes_used = bytes_used - (tuple[BYTES] or 0) + removed_tuple = tuple + end + + -- removes elemenets to provide enough memory + -- returns last removed element or nil + local function makeFreeSpace(bytes) + while size + 1 > max_size or (max_bytes and bytes_used + bytes > max_bytes) do + assert(oldest, "not enough storage for cache") + del(oldest[KEY], oldest) + end + end + + local function get(_, key) + local tuple = map[key] + if not tuple then + return nil + end + cut(tuple) + setNewest(tuple) + return tuple[VALUE] + end + + -- 增删都用这个接口,有点歧义 + local function set(_, key, value, bytes) + local tuple = map[key] + if tuple then + del(key, tuple) + end + if value ~= nil then + -- the value is not removed + bytes = max_bytes and (bytes or #value) or 0 + makeFreeSpace(bytes) + local tuple1 = removed_tuple or {} + map[key] = tuple1 + tuple1[VALUE] = value + tuple1[KEY] = key + tuple1[BYTES] = max_bytes and bytes + size = size + 1 + bytes_used = bytes_used + bytes + setNewest(tuple1) + else + assert(key ~= nil, "Key may not be nil") + end + removed_tuple = nil + end + + local function delete(_, key) + return set(_, key, nil) + end + + local function mynext(_, prev_key) + local tuple + if prev_key then + tuple = map[prev_key][NEXT] + else + tuple = newest + end + if tuple then + return tuple[KEY], tuple[VALUE] + else + return nil + end + end + + -- returns iterator for keys and values + local function lru_pairs() + return mynext, nil, nil + end + + local mt = { + __index = { + get = get, + set = set, + delete = delete, + pairs = lru_pairs, + }, + __pairs = lru_pairs, + } + + return setmetatable({}, mt) +end + +return LRU diff --git a/framework/lualib/thirdparty/ltrace.lua b/framework/lualib/thirdparty/ltrace.lua new file mode 100644 index 0000000..f57b526 --- /dev/null +++ b/framework/lualib/thirdparty/ltrace.lua @@ -0,0 +1,308 @@ +--[[ + https://github.com/sundream/ltrace +--]] +local ltrace_getenv +local getstack_from +local getstack +local dumpvalue +local dumpkey +local dumptable +local dumpframe +local dumpstack +local getfulltrace + +local mTABLE_MAX_DEEP = 3 +local mVALUE_MAX_LEN = 5120 +local traceback = {} + +-- local setfenv = +-- _G.setfenv or +-- function(f, t) +-- f = (type(f) == "function" and f or debug.getinfo(f + 1, "f").func) +-- local name +-- local up = 0 +-- repeat +-- up = up + 1 +-- name = debug.getupvalue(f, up) +-- until name == "_ENV" or name == nil +-- if name then +-- debug.upvaluejoin( +-- f, +-- up, +-- function() +-- return name +-- end, +-- 1 +-- ) -- use unique upvalue +-- debug.setupvalue(f, up, t) +-- end +-- return f +-- end + +local getfenv = _G.getfenv or function(f) + f = (type(f) == "function" and f or debug.getinfo(f + 1, "f").func) + local name, val + local up = 0 + repeat + up = up + 1 + name, val = debug.getupvalue(f, up) + until name == "_ENV" or name == nil + return val + end + +local function canPrint(k, v) + if type(v) ~= "table" then + return true + end + if v == _G then + return false + end + return true +end + +ltrace_getenv = function(realframe) + local env = {} + local indexmap = {} + local i = 1 + local funcinfo = debug.getinfo(realframe, "nlSf") + if not funcinfo then + return + end + local k, v = debug.getlocal(realframe, i) + while k do + indexmap[k] = i + env[k] = v + i = i + 1 + k, v = debug.getlocal(realframe, i) + end + + setmetatable(env, {__index = getfenv(funcinfo.func)}) + return env, funcinfo, indexmap +end + +getstack_from = function(callfile, callline, maxframesz) + assert(callfile and callline) + local realframe = 0 + local framestack = {} + local env, funcinfo, indexmap = ltrace_getenv(realframe) + while funcinfo do + if funcinfo.currentline == callline and funcinfo.short_src == callfile then + break + end + realframe = realframe + 1 + env, funcinfo, indexmap = ltrace_getenv(realframe) + end + while funcinfo do + realframe = realframe + 1 + env, funcinfo, indexmap = ltrace_getenv(realframe) + if not funcinfo then + break + end + table.insert( + framestack, + { + realframe = realframe, + env = env, + funcinfo = funcinfo, + indexmap = indexmap + } + ) + if maxframesz and #framestack >= maxframesz then + break + end + end + return framestack +end + +local function get_params(func) + local info = debug.getinfo(func, "u") + if not info then + return + end + local argNameList = {} + for i = 1, info.nparams do + local name = debug.getlocal(func, i) + table.insert(argNameList, name) + end + if info.isvararg then + table.insert(argNameList, "...") + end + return argNameList, info.nparams, info.isvararg +end + +local uncomparedTypeMap = { + ["table"] = true, + ["userdata"] = true, + ["function"] = true, + ["boolean"] = true, + ["thread"] = true +} + +local function compare(a, b) + local ta = type(a) + local tb = type(b) + if ta ~= tb or uncomparedTypeMap[ta] or uncomparedTypeMap[tb] then + return tostring(a) < tostring(b) + end + return a < b +end + +local function pairs_orderly(t, comp) + comp = comp or compare + local keys = {} + for k, v in pairs(t) do + table.insert(keys, k) + end + local size = #keys + table.sort(keys, comp) + + local i = 0 + return function(tbl, k) + i = i + 1 + if i > size then + return + end + return keys[i], t[keys[i]] + end +end + +local ignoreMap = {} + +dumptable = function(value, depth) + assert(type(value) == "table") + local tostr + local meta = getmetatable(value) + if meta and meta.__tostring then + tostr = meta.__tostring + elseif value.__tostring then + tostr = value.__tostring + end + if tostr then + return tostr(value) + end + local rettbl = {} + depth = (depth or 0) + 1 + if depth > mTABLE_MAX_DEEP then + return "{...}" + end + + table.insert(rettbl, "{") + local content = {} + for k, v in pairs_orderly(value) do + if not ignoreMap[k] and canPrint(k, v) then + local line = {} + table.insert(line, dumpkey(k, depth) .. "=") + table.insert(line, dumpvalue(v, depth)) + table.insert(content, table.concat(line)) + end + end + table.insert(rettbl, table.concat(content, ", ")) + table.insert(rettbl, "}") + return table.concat(rettbl) +end + +dumpkey = function(key, depth) + local vtype = type(key) + if vtype == "table" then + return "[" .. dumptable(key, depth) .. "]" + elseif vtype == "string" then + if key:match("^%d") or key:match("%w+") ~= key then + return "[" .. string.format("%q", key) .. "]" + end + return tostring(key) + end + return "[" .. tostring(key) .. "]" +end + +dumpvalue = function(v, depth) + local vtype = type(v) + if vtype == "table" then + return dumptable(v, depth) + elseif vtype == "string" then + return string.format("%q", v) + elseif vtype == "number" or vtype == "boolean" then + return tostring(v) + end + return string.format("<%s>", tostring(v)) +end + +dumpframe = function(frameidx, env, funcinfo, indexmap) + local fix = -1 + if funcinfo.what ~= "Lua" then + return string.format("%d[C] : in <%s>", frameidx + fix, funcinfo.name or funcinfo.what or funcinfo.namewhat) + end + + local out = {} + local args = get_params(funcinfo.func) + local funcstr = "" + if funcinfo.name then + funcstr = string.format("in <%s(%s)>", funcinfo.name, table.concat(args, ",")) + end + table.insert( + out, + string.format("%d @%s:%d %s", frameidx + fix, funcinfo.short_src or "", funcinfo.currentline, funcstr) + ) + + local valuelines = {} + local function compare(k1, k2) + return indexmap[k1] < indexmap[k2] + end + for k, v in pairs_orderly(env, compare) do + if canPrint(k, v) then + local vs = dumpvalue(v) + if #vs > mVALUE_MAX_LEN then + vs = vs:sub(1, mVALUE_MAX_LEN) .. "..." + end + -- table.insert(valuelines, string.format("\t%s<%s>:%s", k, indexmap[k] or "", vs)) + table.insert(valuelines, string.format("\t%s:%s", k, vs)) + end + end + + table.insert(out, table.concat(valuelines, "\n")) + return table.concat(out, "\n") +end + +dumpstack = function(stack) + local list = {} + for i, v in pairs(stack) do + local s = dumpframe(i, v.env, v.funcinfo, v.indexmap) + table.insert(list, s) + end + return list +end + +getstack = function(level, maxframesz) + level = level or 2 + local info = debug.getinfo(level, "nlSf") + return getstack_from(info.short_src, info.currentline, maxframesz) +end + +getfulltrace = function(level, maxframesz) + local fullstack = getstack(level, maxframesz) + local list = dumpstack(fullstack) + local ret = {} + for _, v in pairs(list) do + table.insert(ret, v) + end + return table.concat(ret, "\n") +end + +local function settdmaxdeep(depth) + mTABLE_MAX_DEEP = depth or mTABLE_MAX_DEEP +end + +local function setvdmaxlen(len) + mVALUE_MAX_LEN = len or mVALUE_MAX_LEN +end + +traceback.setvdmaxlen = setvdmaxlen +traceback.settdmaxdeep = settdmaxdeep +traceback.getstack_from = getstack_from +traceback.getstack = getstack +traceback.dumpvalue = dumpvalue +traceback.dumptable = dumptable +traceback.dumpframe = dumpframe +traceback.dumpstack = dumpstack +traceback.getfulltrace = getfulltrace + +return traceback diff --git a/framework/lualib/thirdparty/luaunit/luaunit.lua b/framework/lualib/thirdparty/luaunit/luaunit.lua new file mode 100755 index 0000000..4741478 --- /dev/null +++ b/framework/lualib/thirdparty/luaunit/luaunit.lua @@ -0,0 +1,3453 @@ +--[[ + luaunit.lua + +Description: A unit testing framework +Homepage: https://github.com/bluebird75/luaunit +Development by Philippe Fremy +Based on initial work of Ryu, Gwang (http://www.gpgstudy.com/gpgiki/LuaUnit) +License: BSD License, see LICENSE.txt +]]-- + +require("math") +local M={} + +-- private exported functions (for testing) +M.private = {} + +M.VERSION='3.4' +M._VERSION=M.VERSION -- For LuaUnit v2 compatibility + +-- a version which distinguish between regular Lua and LuaJit +M._LUAVERSION = (jit and jit.version) or _VERSION + +--[[ Some people like assertEquals( actual, expected ) and some people prefer +assertEquals( expected, actual ). +]]-- +M.ORDER_ACTUAL_EXPECTED = true +M.PRINT_TABLE_REF_IN_ERROR_MSG = false +M.LINE_LENGTH = 80 +M.TABLE_DIFF_ANALYSIS_THRESHOLD = 10 -- display deep analysis for more than 10 items +M.LIST_DIFF_ANALYSIS_THRESHOLD = 10 -- display deep analysis for more than 10 items + +-- this setting allow to remove entries from the stack-trace, for +-- example to hide a call to a framework which would be calling luaunit +M.STRIP_EXTRA_ENTRIES_IN_STACK_TRACE = 0 + +--[[ EPS is meant to help with Lua's floating point math in simple corner +cases like almostEquals(1.1-0.1, 1), which may not work as-is (e.g. on numbers +with rational binary representation) if the user doesn't provide some explicit +error margin. + +The default margin used by almostEquals() in such cases is EPS; and since +Lua may be compiled with different numeric precisions (single vs. double), we +try to select a useful default for it dynamically. Note: If the initial value +is not acceptable, it can be changed by the user to better suit specific needs. + +See also: https://en.wikipedia.org/wiki/Machine_epsilon +]] +M.EPS = 2^-52 -- = machine epsilon for "double", ~2.22E-16 +if math.abs(1.1 - 1 - 0.1) > M.EPS then + -- rounding error is above EPS, assume single precision + M.EPS = 2^-23 -- = machine epsilon for "float", ~1.19E-07 +end + +-- set this to false to debug luaunit +local STRIP_LUAUNIT_FROM_STACKTRACE = true + +M.VERBOSITY_DEFAULT = 10 +M.VERBOSITY_LOW = 1 +M.VERBOSITY_QUIET = 0 +M.VERBOSITY_VERBOSE = 20 +M.DEFAULT_DEEP_ANALYSIS = nil +M.FORCE_DEEP_ANALYSIS = true +M.DISABLE_DEEP_ANALYSIS = false + +-- set EXPORT_ASSERT_TO_GLOBALS to have all asserts visible as global values +-- EXPORT_ASSERT_TO_GLOBALS = true + +-- we need to keep a copy of the script args before it is overriden +local cmdline_argv = rawget(_G, "arg") + +M.FAILURE_PREFIX = 'LuaUnit test FAILURE: ' -- prefix string for failed tests +M.SUCCESS_PREFIX = 'LuaUnit test SUCCESS: ' -- prefix string for successful tests finished early +M.SKIP_PREFIX = 'LuaUnit test SKIP: ' -- prefix string for skipped tests + + + +M.USAGE=[[Usage: lua [options] [testname1 [testname2] ... ] +Options: + -h, --help: Print this help + --version: Print version information + -v, --verbose: Increase verbosity + -q, --quiet: Set verbosity to minimum + -e, --error: Stop on first error + -f, --failure: Stop on first failure or error + -s, --shuffle: Shuffle tests before running them + -o, --output OUTPUT: Set output type to OUTPUT + Possible values: text, tap, junit, nil + -n, --name NAME: For junit only, mandatory name of xml file + -r, --repeat NUM: Execute all tests NUM times, e.g. to trig the JIT + -p, --pattern PATTERN: Execute all test names matching the Lua PATTERN + May be repeated to include several patterns + Make sure you escape magic chars like +? with % + -x, --exclude PATTERN: Exclude all test names matching the Lua PATTERN + May be repeated to exclude several patterns + Make sure you escape magic chars like +? with % + testname1, testname2, ... : tests to run in the form of testFunction, + TestClass or TestClass.testMethod + +You may also control LuaUnit options with the following environment variables: +* LUAUNIT_OUTPUT: same as --output +* LUAUNIT_JUNIT_FNAME: same as --name ]] + +---------------------------------------------------------------- +-- +-- general utility functions +-- +---------------------------------------------------------------- + +--[[ Note on catching exit + +I have seen the case where running a big suite of test cases and one of them would +perform a os.exit(0), making the outside world think that the full test suite was executed +successfully. + +This is an attempt to mitigate this problem: we override os.exit() to now let a test +exit the framework while we are running. When we are not running, it behaves normally. +]] + +M.oldOsExit = os.exit +os.exit = function(...) + if M.LuaUnit and #M.LuaUnit.instances ~= 0 then + local msg = [[You are trying to exit but there is still a running instance of LuaUnit. +LuaUnit expects to run until the end before exiting with a complete status of successful/failed tests. + +To force exit LuaUnit while running, please call before os.exit (assuming lu is the luaunit module loaded): + + lu.unregisterCurrentSuite() + +]] + M.private.error_fmt(2, msg) + end + M.oldOsExit(...) +end + +local function pcall_or_abort(func, ...) + -- unpack is a global function for Lua 5.1, otherwise use table.unpack + local unpack = rawget(_G, "unpack") or table.unpack + local result = {pcall(func, ...)} + if not result[1] then + -- an error occurred + print(result[2]) -- error message + print() + print(M.USAGE) + os.exit(-1) + end + return unpack(result, 2) +end + +local crossTypeOrdering = { + number = 1, boolean = 2, string = 3, table = 4, other = 5 +} +local crossTypeComparison = { + number = function(a, b) return a < b end, + string = function(a, b) return a < b end, + other = function(a, b) return tostring(a) < tostring(b) end, +} + +local function crossTypeSort(a, b) + local type_a, type_b = type(a), type(b) + if type_a == type_b then + local func = crossTypeComparison[type_a] or crossTypeComparison.other + return func(a, b) + end + type_a = crossTypeOrdering[type_a] or crossTypeOrdering.other + type_b = crossTypeOrdering[type_b] or crossTypeOrdering.other + return type_a < type_b +end + +local function __genSortedIndex( t ) + -- Returns a sequence consisting of t's keys, sorted. + local sortedIndex = {} + + for key,_ in pairs(t) do + table.insert(sortedIndex, key) + end + + table.sort(sortedIndex, crossTypeSort) + return sortedIndex +end +M.private.__genSortedIndex = __genSortedIndex + +local function sortedNext(state, control) + -- Equivalent of the next() function of table iteration, but returns the + -- keys in sorted order (see __genSortedIndex and crossTypeSort). + -- The state is a temporary variable during iteration and contains the + -- sorted key table (state.sortedIdx). It also stores the last index (into + -- the keys) used by the iteration, to find the next one quickly. + local key + + --print("sortedNext: control = "..tostring(control) ) + if control == nil then + -- start of iteration + state.count = #state.sortedIdx + state.lastIdx = 1 + key = state.sortedIdx[1] + return key, state.t[key] + end + + -- normally, we expect the control variable to match the last key used + if control ~= state.sortedIdx[state.lastIdx] then + -- strange, we have to find the next value by ourselves + -- the key table is sorted in crossTypeSort() order! -> use bisection + local lower, upper = 1, state.count + repeat + state.lastIdx = math.modf((lower + upper) / 2) + key = state.sortedIdx[state.lastIdx] + if key == control then + break -- key found (and thus prev index) + end + if crossTypeSort(key, control) then + -- key < control, continue search "right" (towards upper bound) + lower = state.lastIdx + 1 + else + -- key > control, continue search "left" (towards lower bound) + upper = state.lastIdx - 1 + end + until lower > upper + if lower > upper then -- only true if the key wasn't found, ... + state.lastIdx = state.count -- ... so ensure no match in code below + end + end + + -- proceed by retrieving the next value (or nil) from the sorted keys + state.lastIdx = state.lastIdx + 1 + key = state.sortedIdx[state.lastIdx] + if key then + return key, state.t[key] + end + + -- getting here means returning `nil`, which will end the iteration +end + +local function sortedPairs(tbl) + -- Equivalent of the pairs() function on tables. Allows to iterate in + -- sorted order. As required by "generic for" loops, this will return the + -- iterator (function), an "invariant state", and the initial control value. + -- (see http://www.lua.org/pil/7.2.html) + return sortedNext, {t = tbl, sortedIdx = __genSortedIndex(tbl)}, nil +end +M.private.sortedPairs = sortedPairs + +-- seed the random with a strongly varying seed +math.randomseed(math.floor(os.clock()*1E11)) + +local function randomizeTable( t ) + -- randomize the item orders of the table t + for i = #t, 2, -1 do + local j = math.random(i) + if i ~= j then + t[i], t[j] = t[j], t[i] + end + end +end +M.private.randomizeTable = randomizeTable + +local function strsplit(delimiter, text) +-- Split text into a list consisting of the strings in text, separated +-- by strings matching delimiter (which may _NOT_ be a pattern). +-- Example: strsplit(", ", "Anna, Bob, Charlie, Dolores") + if delimiter == "" or delimiter == nil then -- this would result in endless loops + error("delimiter is nil or empty string!") + end + if text == nil then + return nil + end + + local list, pos, first, last = {}, 1 + while true do + first, last = text:find(delimiter, pos, true) + if first then -- found? + table.insert(list, text:sub(pos, first - 1)) + pos = last + 1 + else + table.insert(list, text:sub(pos)) + break + end + end + return list +end +M.private.strsplit = strsplit + +local function hasNewLine( s ) + -- return true if s has a newline + return (string.find(s, '\n', 1, true) ~= nil) +end +M.private.hasNewLine = hasNewLine + +local function prefixString( prefix, s ) + -- Prefix all the lines of s with prefix + return prefix .. string.gsub(s, '\n', '\n' .. prefix) +end +M.private.prefixString = prefixString + +local function strMatch(s, pattern, start, final ) + -- return true if s matches completely the pattern from index start to index end + -- return false in every other cases + -- if start is nil, matches from the beginning of the string + -- if final is nil, matches to the end of the string + start = start or 1 + final = final or string.len(s) + + local foundStart, foundEnd = string.find(s, pattern, start, false) + return foundStart == start and foundEnd == final +end +M.private.strMatch = strMatch + +local function patternFilter(patterns, expr) + -- Run `expr` through the inclusion and exclusion rules defined in patterns + -- and return true if expr shall be included, false for excluded. + -- Inclusion pattern are defined as normal patterns, exclusions + -- patterns start with `!` and are followed by a normal pattern + + -- result: nil = UNKNOWN (not matched yet), true = ACCEPT, false = REJECT + -- default: true if no explicit "include" is found, set to false otherwise + local default, result = true, nil + + if patterns ~= nil then + for _, pattern in ipairs(patterns) do + local exclude = pattern:sub(1,1) == '!' + if exclude then + pattern = pattern:sub(2) + else + -- at least one include pattern specified, a match is required + default = false + end + -- print('pattern: ',pattern) + -- print('exclude: ',exclude) + -- print('default: ',default) + + if string.find(expr, pattern) then + -- set result to false when excluding, true otherwise + result = not exclude + end + end + end + + if result ~= nil then + return result + end + return default +end +M.private.patternFilter = patternFilter + +local function xmlEscape( s ) + -- Return s escaped for XML attributes + -- escapes table: + -- " " + -- ' ' + -- < < + -- > > + -- & & + + return string.gsub( s, '.', { + ['&'] = "&", + ['"'] = """, + ["'"] = "'", + ['<'] = "<", + ['>'] = ">", + } ) +end +M.private.xmlEscape = xmlEscape + +local function xmlCDataEscape( s ) + -- Return s escaped for CData section, escapes: "]]>" + return string.gsub( s, ']]>', ']]>' ) +end +M.private.xmlCDataEscape = xmlCDataEscape + + +local function lstrip( s ) + --[[Return s with all leading white spaces and tabs removed]] + local idx = 0 + while idx < s:len() do + idx = idx + 1 + local c = s:sub(idx,idx) + if c ~= ' ' and c ~= '\t' then + break + end + end + return s:sub(idx) +end +M.private.lstrip = lstrip + +local function extractFileLineInfo( s ) + --[[ From a string in the form "(leading spaces) dir1/dir2\dir3\file.lua:linenb: msg" + + Return the "file.lua:linenb" information + ]] + local s2 = lstrip(s) + local firstColon = s2:find(':', 1, true) + if firstColon == nil then + -- string is not in the format file:line: + return s + end + local secondColon = s2:find(':', firstColon+1, true) + if secondColon == nil then + -- string is not in the format file:line: + return s + end + + return s2:sub(1, secondColon-1) +end +M.private.extractFileLineInfo = extractFileLineInfo + + +local function stripLuaunitTrace2( stackTrace, errMsg ) + --[[ + -- Example of a traceback: + < + [C]: in function 'xpcall' + ./luaunit.lua:1449: in function 'protectedCall' + ./luaunit.lua:1508: in function 'execOneFunction' + ./luaunit.lua:1596: in function 'runSuiteByInstances' + ./luaunit.lua:1660: in function 'runSuiteByNames' + ./luaunit.lua:1736: in function 'runSuite' + example_with_luaunit.lua:140: in main chunk + [C]: in ?>> + error message: <> + + Other example: + < + [C]: in function 'xpcall' + ./luaunit.lua:1517: in function 'protectedCall' + ./luaunit.lua:1578: in function 'execOneFunction' + ./luaunit.lua:1677: in function 'runSuiteByInstances' + ./luaunit.lua:1730: in function 'runSuiteByNames' + ./luaunit.lua:1806: in function 'runSuite' + example_with_luaunit.lua:140: in main chunk + [C]: in ?>> + error message: <> + + < + [C]: in function 'xpcall' + luaunit2/luaunit.lua:1532: in function 'protectedCall' + luaunit2/luaunit.lua:1591: in function 'execOneFunction' + luaunit2/luaunit.lua:1679: in function 'runSuiteByInstances' + luaunit2/luaunit.lua:1743: in function 'runSuiteByNames' + luaunit2/luaunit.lua:1819: in function 'runSuite' + luaunit2/example_with_luaunit.lua:140: in main chunk + [C]: in ?>> + error message: <> + + + -- first line is "stack traceback": KEEP + -- next line may be luaunit line: REMOVE + -- next lines are call in the program under testOk: REMOVE + -- next lines are calls from luaunit to call the program under test: KEEP + + -- Strategy: + -- keep first line + -- remove lines that are part of luaunit + -- kepp lines until we hit a luaunit line + + The strategy for stripping is: + * keep first line "stack traceback:" + * part1: + * analyse all lines of the stack from bottom to top of the stack (first line to last line) + * extract the "file:line:" part of the line + * compare it with the "file:line" part of the error message + * if it does not match strip the line + * if it matches, keep the line and move to part 2 + * part2: + * anything NOT starting with luaunit.lua is the interesting part of the stack trace + * anything starting again with luaunit.lua is part of the test launcher and should be stripped out + ]] + + local function isLuaunitInternalLine( s ) + -- return true if line of stack trace comes from inside luaunit + return s:find('[/\\]luaunit%.lua:%d+: ') ~= nil + end + + -- print( '<<'..stackTrace..'>>' ) + + local t = strsplit( '\n', stackTrace ) + -- print( prettystr(t) ) + + local idx = 2 + + local errMsgFileLine = extractFileLineInfo(errMsg) + -- print('emfi="'..errMsgFileLine..'"') + + -- remove lines that are still part of luaunit + while t[idx] and extractFileLineInfo(t[idx]) ~= errMsgFileLine do + -- print('Removing : '..t[idx] ) + table.remove(t, idx) + end + + -- keep lines until we hit luaunit again + while t[idx] and (not isLuaunitInternalLine(t[idx])) do + -- print('Keeping : '..t[idx] ) + idx = idx + 1 + end + + -- remove remaining luaunit lines + while t[idx] do + -- print('Removing2 : '..t[idx] ) + table.remove(t, idx) + end + + -- print( prettystr(t) ) + return table.concat( t, '\n') + +end +M.private.stripLuaunitTrace2 = stripLuaunitTrace2 + + +local function prettystr_sub(v, indentLevel, printTableRefs, cycleDetectTable ) + local type_v = type(v) + if "string" == type_v then + -- use clever delimiters according to content: + -- enclose with single quotes if string contains ", but no ' + if v:find('"', 1, true) and not v:find("'", 1, true) then + return "'" .. v .. "'" + end + -- use double quotes otherwise, escape embedded " + return '"' .. v:gsub('"', '\\"') .. '"' + + elseif "table" == type_v then + --if v.__class__ then + -- return string.gsub( tostring(v), 'table', v.__class__ ) + --end + return M.private._table_tostring(v, indentLevel, printTableRefs, cycleDetectTable) + + elseif "number" == type_v then + -- eliminate differences in formatting between various Lua versions + if v ~= v then + return "#NaN" -- "not a number" + end + if v == math.huge then + return "#Inf" -- "infinite" + end + if v == -math.huge then + return "-#Inf" + end + if _VERSION == "Lua 5.3" then + local i = math.tointeger(v) + if i then + return tostring(i) + end + end + end + + return tostring(v) +end + +local function prettystr( v ) + --[[ Pretty string conversion, to display the full content of a variable of any type. + + * string are enclosed with " by default, or with ' if string contains a " + * tables are expanded to show their full content, with indentation in case of nested tables + ]]-- + local cycleDetectTable = {} + local s = prettystr_sub(v, 1, M.PRINT_TABLE_REF_IN_ERROR_MSG, cycleDetectTable) + if cycleDetectTable.detected and not M.PRINT_TABLE_REF_IN_ERROR_MSG then + -- some table contain recursive references, + -- so we must recompute the value by including all table references + -- else the result looks like crap + cycleDetectTable = {} + s = prettystr_sub(v, 1, true, cycleDetectTable) + end + return s +end +M.prettystr = prettystr + +function M.adjust_err_msg_with_iter( err_msg, iter_msg ) + --[[ Adjust the error message err_msg: trim the FAILURE_PREFIX or SUCCESS_PREFIX information if needed, + add the iteration message if any and return the result. + + err_msg: string, error message captured with pcall + iter_msg: a string describing the current iteration ("iteration N") or nil + if there is no iteration in this test. + + Returns: (new_err_msg, test_status) + new_err_msg: string, adjusted error message, or nil in case of success + test_status: M.NodeStatus.FAIL, SUCCESS or ERROR according to the information + contained in the error message. + ]] + if iter_msg then + iter_msg = iter_msg..', ' + else + iter_msg = '' + end + + local RE_FILE_LINE = '.*:%d+: ' + + -- error message is not necessarily a string, + -- so convert the value to string with prettystr() + if type( err_msg ) ~= 'string' then + err_msg = prettystr( err_msg ) + end + + if (err_msg:find( M.SUCCESS_PREFIX ) == 1) or err_msg:match( '('..RE_FILE_LINE..')' .. M.SUCCESS_PREFIX .. ".*" ) then + -- test finished early with success() + return nil, M.NodeStatus.SUCCESS + end + + if (err_msg:find( M.SKIP_PREFIX ) == 1) or (err_msg:match( '('..RE_FILE_LINE..')' .. M.SKIP_PREFIX .. ".*" ) ~= nil) then + -- substitute prefix by iteration message + err_msg = err_msg:gsub('.*'..M.SKIP_PREFIX, iter_msg, 1) + -- print("failure detected") + return err_msg, M.NodeStatus.SKIP + end + + if (err_msg:find( M.FAILURE_PREFIX ) == 1) or (err_msg:match( '('..RE_FILE_LINE..')' .. M.FAILURE_PREFIX .. ".*" ) ~= nil) then + -- substitute prefix by iteration message + err_msg = err_msg:gsub(M.FAILURE_PREFIX, iter_msg, 1) + -- print("failure detected") + return err_msg, M.NodeStatus.FAIL + end + + + + -- print("error detected") + -- regular error, not a failure + if iter_msg then + local match + -- "./test\\test_luaunit.lua:2241: some error msg + match = err_msg:match( '(.*:%d+: ).*' ) + if match then + err_msg = err_msg:gsub( match, match .. iter_msg ) + else + -- no file:line: infromation, just add the iteration info at the beginning of the line + err_msg = iter_msg .. err_msg + end + end + return err_msg, M.NodeStatus.ERROR +end + +local function tryMismatchFormatting( table_a, table_b, doDeepAnalysis, margin ) + --[[ + Prepares a nice error message when comparing tables, performing a deeper + analysis. + + Arguments: + * table_a, table_b: tables to be compared + * doDeepAnalysis: + M.DEFAULT_DEEP_ANALYSIS: (the default if not specified) perform deep analysis only for big lists and big dictionnaries + M.FORCE_DEEP_ANALYSIS : always perform deep analysis + M.DISABLE_DEEP_ANALYSIS: never perform deep analysis + * margin: supplied only for almost equality + + Returns: {success, result} + * success: false if deep analysis could not be performed + in this case, just use standard assertion message + * result: if success is true, a multi-line string with deep analysis of the two lists + ]] + + -- check if table_a & table_b are suitable for deep analysis + if type(table_a) ~= 'table' or type(table_b) ~= 'table' then + return false + end + + if doDeepAnalysis == M.DISABLE_DEEP_ANALYSIS then + return false + end + + local len_a, len_b, isPureList = #table_a, #table_b, true + + for k1, v1 in pairs(table_a) do + if type(k1) ~= 'number' or k1 > len_a then + -- this table a mapping + isPureList = false + break + end + end + + if isPureList then + for k2, v2 in pairs(table_b) do + if type(k2) ~= 'number' or k2 > len_b then + -- this table a mapping + isPureList = false + break + end + end + end + + if isPureList and math.min(len_a, len_b) < M.LIST_DIFF_ANALYSIS_THRESHOLD then + if not (doDeepAnalysis == M.FORCE_DEEP_ANALYSIS) then + return false + end + end + + if isPureList then + return M.private.mismatchFormattingPureList( table_a, table_b, margin ) + else + -- only work on mapping for the moment + -- return M.private.mismatchFormattingMapping( table_a, table_b, doDeepAnalysis ) + return false + end +end +M.private.tryMismatchFormatting = tryMismatchFormatting + +local function getTaTbDescr() + if not M.ORDER_ACTUAL_EXPECTED then + return 'expected', 'actual' + end + return 'actual', 'expected' +end + +local function extendWithStrFmt( res, ... ) + table.insert( res, string.format( ... ) ) +end + +local function mismatchFormattingMapping( table_a, table_b, doDeepAnalysis ) + --[[ + Prepares a nice error message when comparing tables which are not pure lists, performing a deeper + analysis. + + Returns: {success, result} + * success: false if deep analysis could not be performed + in this case, just use standard assertion message + * result: if success is true, a multi-line string with deep analysis of the two lists + ]] + + -- disable for the moment + --[[ + local result = {} + local descrTa, descrTb = getTaTbDescr() + + local keysCommon = {} + local keysOnlyTa = {} + local keysOnlyTb = {} + local keysDiffTaTb = {} + + local k, v + + for k,v in pairs( table_a ) do + if is_equal( v, table_b[k] ) then + table.insert( keysCommon, k ) + else + if table_b[k] == nil then + table.insert( keysOnlyTa, k ) + else + table.insert( keysDiffTaTb, k ) + end + end + end + + for k,v in pairs( table_b ) do + if not is_equal( v, table_a[k] ) and table_a[k] == nil then + table.insert( keysOnlyTb, k ) + end + end + + local len_a = #keysCommon + #keysDiffTaTb + #keysOnlyTa + local len_b = #keysCommon + #keysDiffTaTb + #keysOnlyTb + local limited_display = (len_a < 5 or len_b < 5) + + if math.min(len_a, len_b) < M.TABLE_DIFF_ANALYSIS_THRESHOLD then + return false + end + + if not limited_display then + if len_a == len_b then + extendWithStrFmt( result, 'Table A (%s) and B (%s) both have %d items', descrTa, descrTb, len_a ) + else + extendWithStrFmt( result, 'Table A (%s) has %d items and table B (%s) has %d items', descrTa, len_a, descrTb, len_b ) + end + + if #keysCommon == 0 and #keysDiffTaTb == 0 then + table.insert( result, 'Table A and B have no keys in common, they are totally different') + else + local s_other = 'other ' + if #keysCommon then + extendWithStrFmt( result, 'Table A and B have %d identical items', #keysCommon ) + else + table.insert( result, 'Table A and B have no identical items' ) + s_other = '' + end + + if #keysDiffTaTb ~= 0 then + result[#result] = string.format( '%s and %d items differing present in both tables', result[#result], #keysDiffTaTb) + else + result[#result] = string.format( '%s and no %sitems differing present in both tables', result[#result], s_other, #keysDiffTaTb) + end + end + + extendWithStrFmt( result, 'Table A has %d keys not present in table B and table B has %d keys not present in table A', #keysOnlyTa, #keysOnlyTb ) + end + + local function keytostring(k) + if "string" == type(k) and k:match("^[_%a][_%w]*$") then + return k + end + return prettystr(k) + end + + if #keysDiffTaTb ~= 0 then + table.insert( result, 'Items differing in A and B:') + for k,v in sortedPairs( keysDiffTaTb ) do + extendWithStrFmt( result, ' - A[%s]: %s', keytostring(v), prettystr(table_a[v]) ) + extendWithStrFmt( result, ' + B[%s]: %s', keytostring(v), prettystr(table_b[v]) ) + end + end + + if #keysOnlyTa ~= 0 then + table.insert( result, 'Items only in table A:' ) + for k,v in sortedPairs( keysOnlyTa ) do + extendWithStrFmt( result, ' - A[%s]: %s', keytostring(v), prettystr(table_a[v]) ) + end + end + + if #keysOnlyTb ~= 0 then + table.insert( result, 'Items only in table B:' ) + for k,v in sortedPairs( keysOnlyTb ) do + extendWithStrFmt( result, ' + B[%s]: %s', keytostring(v), prettystr(table_b[v]) ) + end + end + + if #keysCommon ~= 0 then + table.insert( result, 'Items common to A and B:') + for k,v in sortedPairs( keysCommon ) do + extendWithStrFmt( result, ' = A and B [%s]: %s', keytostring(v), prettystr(table_a[v]) ) + end + end + + return true, table.concat( result, '\n') + ]] +end +M.private.mismatchFormattingMapping = mismatchFormattingMapping + +local function mismatchFormattingPureList( table_a, table_b, margin ) + --[[ + Prepares a nice error message when comparing tables which are lists, performing a deeper + analysis. + + margin is supplied only for almost equality + + Returns: {success, result} + * success: false if deep analysis could not be performed + in this case, just use standard assertion message + * result: if success is true, a multi-line string with deep analysis of the two lists + ]] + local result, descrTa, descrTb = {}, getTaTbDescr() + + local len_a, len_b, refa, refb = #table_a, #table_b, '', '' + if M.PRINT_TABLE_REF_IN_ERROR_MSG then + refa, refb = string.format( '<%s> ', M.private.table_ref(table_a)), string.format('<%s> ', M.private.table_ref(table_b) ) + end + local longest, shortest = math.max(len_a, len_b), math.min(len_a, len_b) + local deltalv = longest - shortest + + local commonUntil = shortest + for i = 1, shortest do + if not M.private.is_table_equals(table_a[i], table_b[i], margin) then + commonUntil = i - 1 + break + end + end + + local commonBackTo = shortest - 1 + for i = 0, shortest - 1 do + if not M.private.is_table_equals(table_a[len_a-i], table_b[len_b-i], margin) then + commonBackTo = i - 1 + break + end + end + + + table.insert( result, 'List difference analysis:' ) + if len_a == len_b then + -- TODO: handle expected/actual naming + extendWithStrFmt( result, '* lists %sA (%s) and %sB (%s) have the same size', refa, descrTa, refb, descrTb ) + else + extendWithStrFmt( result, '* list sizes differ: list %sA (%s) has %d items, list %sB (%s) has %d items', refa, descrTa, len_a, refb, descrTb, len_b ) + end + + extendWithStrFmt( result, '* lists A and B start differing at index %d', commonUntil+1 ) + if commonBackTo >= 0 then + if deltalv > 0 then + extendWithStrFmt( result, '* lists A and B are equal again from index %d for A, %d for B', len_a-commonBackTo, len_b-commonBackTo ) + else + extendWithStrFmt( result, '* lists A and B are equal again from index %d', len_a-commonBackTo ) + end + end + + local function insertABValue(ai, bi) + bi = bi or ai + if M.private.is_table_equals( table_a[ai], table_b[bi], margin) then + return extendWithStrFmt( result, ' = A[%d], B[%d]: %s', ai, bi, prettystr(table_a[ai]) ) + else + extendWithStrFmt( result, ' - A[%d]: %s', ai, prettystr(table_a[ai])) + extendWithStrFmt( result, ' + B[%d]: %s', bi, prettystr(table_b[bi])) + end + end + + -- common parts to list A & B, at the beginning + if commonUntil > 0 then + table.insert( result, '* Common parts:' ) + for i = 1, commonUntil do + insertABValue( i ) + end + end + + -- diffing parts to list A & B + if commonUntil < shortest - commonBackTo - 1 then + table.insert( result, '* Differing parts:' ) + for i = commonUntil + 1, shortest - commonBackTo - 1 do + insertABValue( i ) + end + end + + -- display indexes of one list, with no match on other list + if shortest - commonBackTo <= longest - commonBackTo - 1 then + table.insert( result, '* Present only in one list:' ) + for i = shortest - commonBackTo, longest - commonBackTo - 1 do + if len_a > len_b then + extendWithStrFmt( result, ' - A[%d]: %s', i, prettystr(table_a[i]) ) + -- table.insert( result, '+ (no matching B index)') + else + -- table.insert( result, '- no matching A index') + extendWithStrFmt( result, ' + B[%d]: %s', i, prettystr(table_b[i]) ) + end + end + end + + -- common parts to list A & B, at the end + if commonBackTo >= 0 then + table.insert( result, '* Common parts at the end of the lists' ) + for i = longest - commonBackTo, longest do + if len_a > len_b then + insertABValue( i, i-deltalv ) + else + insertABValue( i-deltalv, i ) + end + end + end + + return true, table.concat( result, '\n') +end +M.private.mismatchFormattingPureList = mismatchFormattingPureList + +local function prettystrPairs(value1, value2, suffix_a, suffix_b) + --[[ + This function helps with the recurring task of constructing the "expected + vs. actual" error messages. It takes two arbitrary values and formats + corresponding strings with prettystr(). + + To keep the (possibly complex) output more readable in case the resulting + strings contain line breaks, they get automatically prefixed with additional + newlines. Both suffixes are optional (default to empty strings), and get + appended to the "value1" string. "suffix_a" is used if line breaks were + encountered, "suffix_b" otherwise. + + Returns the two formatted strings (including padding/newlines). + ]] + local str1, str2 = prettystr(value1), prettystr(value2) + if hasNewLine(str1) or hasNewLine(str2) then + -- line break(s) detected, add padding + return "\n" .. str1 .. (suffix_a or ""), "\n" .. str2 + end + return str1 .. (suffix_b or ""), str2 +end +M.private.prettystrPairs = prettystrPairs + +local UNKNOWN_REF = 'table 00-unknown ref' +local ref_generator = { value=1, [UNKNOWN_REF]=0 } + +local function table_ref( t ) + -- return the default tostring() for tables, with the table ID, even if the table has a metatable + -- with the __tostring converter + local ref = '' + local mt = getmetatable( t ) + if mt == nil then + ref = tostring(t) + else + local success, result + success, result = pcall(setmetatable, t, nil) + if not success then + -- protected table, if __tostring is defined, we can + -- not get the reference. And we can not know in advance. + ref = tostring(t) + if not ref:match( 'table: 0?x?[%x]+' ) then + return UNKNOWN_REF + end + else + ref = tostring(t) + setmetatable( t, mt ) + end + end + -- strip the "table: " part + ref = ref:sub(8) + if ref ~= UNKNOWN_REF and ref_generator[ref] == nil then + -- Create a new reference number + ref_generator[ref] = ref_generator.value + ref_generator.value = ref_generator.value+1 + end + if M.PRINT_TABLE_REF_IN_ERROR_MSG then + return string.format('table %02d-%s', ref_generator[ref], ref) + else + return string.format('table %02d', ref_generator[ref]) + end +end +M.private.table_ref = table_ref + +local TABLE_TOSTRING_SEP = ", " +local TABLE_TOSTRING_SEP_LEN = string.len(TABLE_TOSTRING_SEP) + +local function _table_tostring( tbl, indentLevel, printTableRefs, cycleDetectTable ) + printTableRefs = printTableRefs or M.PRINT_TABLE_REF_IN_ERROR_MSG + cycleDetectTable = cycleDetectTable or {} + cycleDetectTable[tbl] = true + + local result, dispOnMultLines = {}, false + + -- like prettystr but do not enclose with "" if the string is just alphanumerical + -- this is better for displaying table keys who are often simple strings + local function keytostring(k) + if "string" == type(k) and k:match("^[_%a][_%w]*$") then + return k + end + return prettystr_sub(k, indentLevel+1, printTableRefs, cycleDetectTable) + end + + local mt = getmetatable( tbl ) + + if mt and mt.__tostring then + -- if table has a __tostring() function in its metatable, use it to display the table + -- else, compute a regular table + result = tostring(tbl) + if type(result) ~= 'string' then + return string.format( '', prettystr(result) ) + end + result = strsplit( '\n', result ) + return M.private._table_tostring_format_multiline_string( result, indentLevel ) + + else + -- no metatable, compute the table representation + + local entry, count, seq_index = nil, 0, 1 + for k, v in sortedPairs( tbl ) do + + -- key part + if k == seq_index then + -- for the sequential part of tables, we'll skip the "=" output + entry = '' + seq_index = seq_index + 1 + elseif cycleDetectTable[k] then + -- recursion in the key detected + cycleDetectTable.detected = true + entry = "<"..table_ref(k)..">=" + else + entry = keytostring(k) .. "=" + end + + -- value part + if cycleDetectTable[v] then + -- recursion in the value detected! + cycleDetectTable.detected = true + entry = entry .. "<"..table_ref(v)..">" + else + entry = entry .. + prettystr_sub( v, indentLevel+1, printTableRefs, cycleDetectTable ) + end + count = count + 1 + result[count] = entry + end + return M.private._table_tostring_format_result( tbl, result, indentLevel, printTableRefs ) + end + +end +M.private._table_tostring = _table_tostring -- prettystr_sub() needs it + +local function _table_tostring_format_multiline_string( tbl_str, indentLevel ) + local indentString = '\n'..string.rep(" ", indentLevel - 1) + return table.concat( tbl_str, indentString ) + +end +M.private._table_tostring_format_multiline_string = _table_tostring_format_multiline_string + + +local function _table_tostring_format_result( tbl, result, indentLevel, printTableRefs ) + -- final function called in _table_to_string() to format the resulting list of + -- string describing the table. + + local dispOnMultLines = false + + -- set dispOnMultLines to true if the maximum LINE_LENGTH would be exceeded with the values + local totalLength = 0 + for k, v in ipairs( result ) do + totalLength = totalLength + string.len( v ) + if totalLength >= M.LINE_LENGTH then + dispOnMultLines = true + break + end + end + + -- set dispOnMultLines to true if the max LINE_LENGTH would be exceeded + -- with the values and the separators. + if not dispOnMultLines then + -- adjust with length of separator(s): + -- two items need 1 sep, three items two seps, ... plus len of '{}' + if #result > 0 then + totalLength = totalLength + TABLE_TOSTRING_SEP_LEN * (#result - 1) + end + dispOnMultLines = (totalLength + 2 >= M.LINE_LENGTH) + end + + -- now reformat the result table (currently holding element strings) + if dispOnMultLines then + local indentString = string.rep(" ", indentLevel - 1) + result = { + "{\n ", + indentString, + table.concat(result, ",\n " .. indentString), + "\n", + indentString, + "}" + } + else + result = {"{", table.concat(result, TABLE_TOSTRING_SEP), "}"} + end + if printTableRefs then + table.insert(result, 1, "<"..table_ref(tbl).."> ") -- prepend table ref + end + return table.concat(result) +end +M.private._table_tostring_format_result = _table_tostring_format_result -- prettystr_sub() needs it + +local function table_findkeyof(t, element) + -- Return the key k of the given element in table t, so that t[k] == element + -- (or `nil` if element is not present within t). Note that we use our + -- 'general' is_equal comparison for matching, so this function should + -- handle table-type elements gracefully and consistently. + if type(t) == "table" then + for k, v in pairs(t) do + if M.private.is_table_equals(v, element) then + return k + end + end + end + return nil +end + +local function _is_table_items_equals(actual, expected ) + local type_a, type_e = type(actual), type(expected) + + if type_a ~= type_e then + return false + + elseif (type_a == 'table') --[[and (type_e == 'table')]] then + for k, v in pairs(actual) do + if table_findkeyof(expected, v) == nil then + return false -- v not contained in expected + end + end + for k, v in pairs(expected) do + if table_findkeyof(actual, v) == nil then + return false -- v not contained in actual + end + end + return true + + elseif actual ~= expected then + return false + end + + return true +end + +--[[ +This is a specialized metatable to help with the bookkeeping of recursions +in _is_table_equals(). It provides an __index table that implements utility +functions for easier management of the table. The "cached" method queries +the state of a specific (actual,expected) pair; and the "store" method sets +this state to the given value. The state of pairs not "seen" / visited is +assumed to be `nil`. +]] +local _recursion_cache_MT = { + __index = { + -- Return the cached value for an (actual,expected) pair (or `nil`) + cached = function(t, actual, expected) + local subtable = t[actual] or {} + return subtable[expected] + end, + + -- Store cached value for a specific (actual,expected) pair. + -- Returns the value, so it's easy to use for a "tailcall" (return ...). + store = function(t, actual, expected, value, asymmetric) + local subtable = t[actual] + if not subtable then + subtable = {} + t[actual] = subtable + end + subtable[expected] = value + + -- Unless explicitly marked "asymmetric": Consider the recursion + -- on (expected,actual) to be equivalent to (actual,expected) by + -- default, and thus cache the value for both. + if not asymmetric then + t:store(expected, actual, value, true) + end + + return value + end + } +} + +local function _is_table_equals(actual, expected, cycleDetectTable, marginForAlmostEqual) + --[[Returns true if both table are equal. + + If argument marginForAlmostEqual is suppied, number comparison is done using alomstEqual instead + of strict equality. + + cycleDetectTable is an internal argument used during recursion on tables. + ]] + --print('_is_table_equals( \n '..prettystr(actual)..'\n , '..prettystr(expected).. + -- '\n , '..prettystr(cycleDetectTable)..'\n , '..prettystr(marginForAlmostEqual)..' )') + + local type_a, type_e = type(actual), type(expected) + + if type_a ~= type_e then + return false -- different types won't match + end + + if type_a == 'number' then + if marginForAlmostEqual ~= nil then + return M.almostEquals(actual, expected, marginForAlmostEqual) + else + return actual == expected + end + elseif type_a ~= 'table' then + -- other types compare directly + return actual == expected + end + + cycleDetectTable = cycleDetectTable or { actual={}, expected={} } + if cycleDetectTable.actual[ actual ] then + -- oh, we hit a cycle in actual + if cycleDetectTable.expected[ expected ] then + -- uh, we hit a cycle at the same time in expected + -- so the two tables have similar structure + return true + end + + -- cycle was hit only in actual, the structure differs from expected + return false + end + + if cycleDetectTable.expected[ expected ] then + -- no cycle in actual, but cycle in expected + -- the structure differ + return false + end + + -- at this point, no table cycle detected, we are + -- seeing this table for the first time + + -- mark the cycle detection + cycleDetectTable.actual[ actual ] = true + cycleDetectTable.expected[ expected ] = true + + + local actualKeysMatched = {} + for k, v in pairs(actual) do + actualKeysMatched[k] = true -- Keep track of matched keys + if not _is_table_equals(v, expected[k], cycleDetectTable, marginForAlmostEqual) then + -- table differs on this key + -- clear the cycle detection before returning + cycleDetectTable.actual[ actual ] = nil + cycleDetectTable.expected[ expected ] = nil + return false + end + end + + for k, v in pairs(expected) do + if not actualKeysMatched[k] then + -- Found a key that we did not see in "actual" -> mismatch + -- clear the cycle detection before returning + cycleDetectTable.actual[ actual ] = nil + cycleDetectTable.expected[ expected ] = nil + return false + end + -- Otherwise actual[k] was already matched against v = expected[k]. + end + + -- all key match, we have a match ! + cycleDetectTable.actual[ actual ] = nil + cycleDetectTable.expected[ expected ] = nil + return true +end +M.private._is_table_equals = _is_table_equals + +local function failure(main_msg, extra_msg_or_nil, level) + -- raise an error indicating a test failure + -- for error() compatibility we adjust "level" here (by +1), to report the + -- calling context + local msg + if type(extra_msg_or_nil) == 'string' and extra_msg_or_nil:len() > 0 then + msg = extra_msg_or_nil .. '\n' .. main_msg + else + msg = main_msg + end + error(M.FAILURE_PREFIX .. msg, (level or 1) + 1 + M.STRIP_EXTRA_ENTRIES_IN_STACK_TRACE) +end + +local function is_table_equals(actual, expected, marginForAlmostEqual) + return _is_table_equals(actual, expected, nil, marginForAlmostEqual) +end +M.private.is_table_equals = is_table_equals + +local function fail_fmt(level, extra_msg_or_nil, ...) + -- failure with printf-style formatted message and given error level + failure(string.format(...), extra_msg_or_nil, (level or 1) + 1) +end +M.private.fail_fmt = fail_fmt + +local function error_fmt(level, ...) + -- printf-style error() + error(string.format(...), (level or 1) + 1 + M.STRIP_EXTRA_ENTRIES_IN_STACK_TRACE) +end +M.private.error_fmt = error_fmt + +---------------------------------------------------------------- +-- +-- assertions +-- +---------------------------------------------------------------- + +local function errorMsgEquality(actual, expected, doDeepAnalysis, margin) + -- margin is supplied only for almost equal verification + + if not M.ORDER_ACTUAL_EXPECTED then + expected, actual = actual, expected + end + if type(expected) == 'string' or type(expected) == 'table' then + local strExpected, strActual = prettystrPairs(expected, actual) + local result = string.format("expected: %s\nactual: %s", strExpected, strActual) + if margin then + result = result .. '\nwere not equal by the margin of: '..prettystr(margin) + end + + -- extend with mismatch analysis if possible: + local success, mismatchResult + success, mismatchResult = tryMismatchFormatting( actual, expected, doDeepAnalysis, margin ) + if success then + result = table.concat( { result, mismatchResult }, '\n' ) + end + return result + end + return string.format("expected: %s, actual: %s", + prettystr(expected), prettystr(actual)) +end + +function M.assertError(f, ...) + -- assert that calling f with the arguments will raise an error + -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error + if pcall( f, ... ) then + failure( "Expected an error when calling function but no error generated", nil, 2 ) + end +end + +function M.fail( msg ) + -- stops a test due to a failure + failure( msg, nil, 2 ) +end + +function M.failIf( cond, msg ) + -- Fails a test with "msg" if condition is true + if cond then + failure( msg, nil, 2 ) + end +end + +function M.skip(msg) + -- skip a running test + error_fmt(2, M.SKIP_PREFIX .. msg) +end + +function M.skipIf( cond, msg ) + -- skip a running test if condition is met + if cond then + error_fmt(2, M.SKIP_PREFIX .. msg) + end +end + +function M.runOnlyIf( cond, msg ) + -- continue a running test if condition is met, else skip it + if not cond then + error_fmt(2, M.SKIP_PREFIX .. prettystr(msg)) + end +end + +function M.success() + -- stops a test with a success + error_fmt(2, M.SUCCESS_PREFIX) +end + +function M.successIf( cond ) + -- stops a test with a success if condition is met + if cond then + error_fmt(2, M.SUCCESS_PREFIX) + end +end + + +------------------------------------------------------------------ +-- Equality assertions +------------------------------------------------------------------ + +function M.assertEquals(actual, expected, extra_msg_or_nil, doDeepAnalysis) + if type(actual) == 'table' and type(expected) == 'table' then + if not is_table_equals(actual, expected) then + failure( errorMsgEquality(actual, expected, doDeepAnalysis), extra_msg_or_nil, 2 ) + end + elseif type(actual) ~= type(expected) then + failure( errorMsgEquality(actual, expected), extra_msg_or_nil, 2 ) + elseif actual ~= expected then + failure( errorMsgEquality(actual, expected), extra_msg_or_nil, 2 ) + end +end + +function M.almostEquals( actual, expected, margin ) + if type(actual) ~= 'number' or type(expected) ~= 'number' or type(margin) ~= 'number' then + error_fmt(3, 'almostEquals: must supply only number arguments.\nArguments supplied: %s, %s, %s', + prettystr(actual), prettystr(expected), prettystr(margin)) + end + if margin < 0 then + error_fmt(3, 'almostEquals: margin must not be negative, current value is ' .. margin) + end + return math.abs(expected - actual) <= margin +end + +function M.assertAlmostEquals( actual, expected, margin, extra_msg_or_nil ) + -- check that two floats are close by margin + margin = margin or M.EPS + if type(margin) ~= 'number' then + error_fmt(2, 'almostEquals: margin must be a number, not %s', prettystr(margin)) + end + + if type(actual) == 'table' and type(expected) == 'table' then + -- handle almost equals for table + if not is_table_equals(actual, expected, margin) then + failure( errorMsgEquality(actual, expected, nil, margin), extra_msg_or_nil, 2 ) + end + elseif type(actual) == 'number' and type(expected) == 'number' and type(margin) == 'number' then + if not M.almostEquals(actual, expected, margin) then + if not M.ORDER_ACTUAL_EXPECTED then + expected, actual = actual, expected + end + local delta = math.abs(actual - expected) + fail_fmt(2, extra_msg_or_nil, 'Values are not almost equal\n' .. + 'Actual: %s, expected: %s, delta %s above margin of %s', + actual, expected, delta, margin) + end + else + error_fmt(3, 'almostEquals: must supply only number or table arguments.\nArguments supplied: %s, %s, %s', + prettystr(actual), prettystr(expected), prettystr(margin)) + end +end + +function M.assertNotEquals(actual, expected, extra_msg_or_nil) + if type(actual) ~= type(expected) then + return + end + + if type(actual) == 'table' and type(expected) == 'table' then + if not is_table_equals(actual, expected) then + return + end + elseif actual ~= expected then + return + end + fail_fmt(2, extra_msg_or_nil, 'Received the not expected value: %s', prettystr(actual)) +end + +function M.assertNotAlmostEquals( actual, expected, margin, extra_msg_or_nil ) + -- check that two floats are not close by margin + margin = margin or M.EPS + if M.almostEquals(actual, expected, margin) then + if not M.ORDER_ACTUAL_EXPECTED then + expected, actual = actual, expected + end + local delta = math.abs(actual - expected) + fail_fmt(2, extra_msg_or_nil, 'Values are almost equal\nActual: %s, expected: %s' .. + ', delta %s below margin of %s', + actual, expected, delta, margin) + end +end + +function M.assertItemsEquals(actual, expected, extra_msg_or_nil) + -- checks that the items of table expected + -- are contained in table actual. Warning, this function + -- is at least O(n^2) + if not _is_table_items_equals(actual, expected ) then + expected, actual = prettystrPairs(expected, actual) + fail_fmt(2, extra_msg_or_nil, 'Content of the tables are not identical:\nExpected: %s\nActual: %s', + expected, actual) + end +end + +------------------------------------------------------------------ +-- String assertion +------------------------------------------------------------------ + +function M.assertStrContains( str, sub, isPattern, extra_msg_or_nil ) + -- this relies on lua string.find function + -- a string always contains the empty string + -- assert( type(str) == 'string', 'Argument 1 of assertStrContains() should be a string.' ) ) + -- assert( type(sub) == 'string', 'Argument 2 of assertStrContains() should be a string.' ) ) + if not string.find(str, sub, 1, not isPattern) then + sub, str = prettystrPairs(sub, str, '\n') + fail_fmt(2, extra_msg_or_nil, 'Could not find %s %s in string %s', + isPattern and 'pattern' or 'substring', sub, str) + end +end + +function M.assertStrIContains( str, sub, extra_msg_or_nil ) + -- this relies on lua string.find function + -- a string always contains the empty string + if not string.find(str:lower(), sub:lower(), 1, true) then + sub, str = prettystrPairs(sub, str, '\n') + fail_fmt(2, extra_msg_or_nil, 'Could not find (case insensitively) substring %s in string %s', + sub, str) + end +end + +function M.assertNotStrContains( str, sub, isPattern, extra_msg_or_nil ) + -- this relies on lua string.find function + -- a string always contains the empty string + if string.find(str, sub, 1, not isPattern) then + sub, str = prettystrPairs(sub, str, '\n') + fail_fmt(2, extra_msg_or_nil, 'Found the not expected %s %s in string %s', + isPattern and 'pattern' or 'substring', sub, str) + end +end + +function M.assertNotStrIContains( str, sub, extra_msg_or_nil ) + -- this relies on lua string.find function + -- a string always contains the empty string + if string.find(str:lower(), sub:lower(), 1, true) then + sub, str = prettystrPairs(sub, str, '\n') + fail_fmt(2, extra_msg_or_nil, 'Found (case insensitively) the not expected substring %s in string %s', + sub, str) + end +end + +function M.assertStrMatches( str, pattern, start, final, extra_msg_or_nil ) + -- Verify a full match for the string + if not strMatch( str, pattern, start, final ) then + pattern, str = prettystrPairs(pattern, str, '\n') + fail_fmt(2, extra_msg_or_nil, 'Could not match pattern %s with string %s', + pattern, str) + end +end + +local function _assertErrorMsgEquals( stripFileAndLine, expectedMsg, func, ... ) + local no_error, error_msg = pcall( func, ... ) + if no_error then + failure( 'No error generated when calling function but expected error: '..M.prettystr(expectedMsg), nil, 3 ) + end + if type(expectedMsg) == "string" and type(error_msg) ~= "string" then + -- table are converted to string automatically + error_msg = tostring(error_msg) + end + local differ = false + if stripFileAndLine then + if error_msg:gsub("^.+:%d+: ", "") ~= expectedMsg then + differ = true + end + else + if error_msg ~= expectedMsg then + local tr = type(error_msg) + local te = type(expectedMsg) + if te == 'table' then + if tr ~= 'table' then + differ = true + else + local ok = pcall(M.assertItemsEquals, error_msg, expectedMsg) + if not ok then + differ = true + end + end + else + differ = true + end + end + end + + if differ then + error_msg, expectedMsg = prettystrPairs(error_msg, expectedMsg) + fail_fmt(3, nil, 'Error message expected: %s\nError message received: %s\n', + expectedMsg, error_msg) + end +end + +function M.assertErrorMsgEquals( expectedMsg, func, ... ) + -- assert that calling f with the arguments will raise an error + -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error + _assertErrorMsgEquals(false, expectedMsg, func, ...) +end + +function M.assertErrorMsgContentEquals(expectedMsg, func, ...) + _assertErrorMsgEquals(true, expectedMsg, func, ...) +end + +function M.assertErrorMsgContains( partialMsg, func, ... ) + -- assert that calling f with the arguments will raise an error + -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error + local no_error, error_msg = pcall( func, ... ) + if no_error then + failure( 'No error generated when calling function but expected error containing: '..prettystr(partialMsg), nil, 2 ) + end + if type(error_msg) ~= "string" then + error_msg = tostring(error_msg) + end + if not string.find( error_msg, partialMsg, nil, true ) then + error_msg, partialMsg = prettystrPairs(error_msg, partialMsg) + fail_fmt(2, nil, 'Error message does not contain: %s\nError message received: %s\n', + partialMsg, error_msg) + end +end + +function M.assertErrorMsgMatches( expectedMsg, func, ... ) + -- assert that calling f with the arguments will raise an error + -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error + local no_error, error_msg = pcall( func, ... ) + if no_error then + failure( 'No error generated when calling function but expected error matching: "'..expectedMsg..'"', nil, 2 ) + end + if type(error_msg) ~= "string" then + error_msg = tostring(error_msg) + end + if not strMatch( error_msg, expectedMsg ) then + expectedMsg, error_msg = prettystrPairs(expectedMsg, error_msg) + fail_fmt(2, nil, 'Error message does not match pattern: %s\nError message received: %s\n', + expectedMsg, error_msg) + end +end + +------------------------------------------------------------------ +-- Type assertions +------------------------------------------------------------------ + +function M.assertEvalToTrue(value, extra_msg_or_nil) + if not value then + failure("expected: a value evaluating to true, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertEvalToFalse(value, extra_msg_or_nil) + if value then + failure("expected: false or nil, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertIsTrue(value, extra_msg_or_nil) + if value ~= true then + failure("expected: true, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertNotIsTrue(value, extra_msg_or_nil) + if value == true then + failure("expected: not true, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertIsFalse(value, extra_msg_or_nil) + if value ~= false then + failure("expected: false, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertNotIsFalse(value, extra_msg_or_nil) + if value == false then + failure("expected: not false, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertIsNil(value, extra_msg_or_nil) + if value ~= nil then + failure("expected: nil, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertNotIsNil(value, extra_msg_or_nil) + if value == nil then + failure("expected: not nil, actual: nil", extra_msg_or_nil, 2) + end +end + +--[[ +Add type assertion functions to the module table M. Each of these functions +takes a single parameter "value", and checks that its Lua type matches the +expected string (derived from the function name): + +M.assertIsXxx(value) -> ensure that type(value) conforms to "xxx" +]] +for _, funcName in ipairs( + {'assertIsNumber', 'assertIsString', 'assertIsTable', 'assertIsBoolean', + 'assertIsFunction', 'assertIsUserdata', 'assertIsThread'} +) do + local typeExpected = funcName:match("^assertIs([A-Z]%a*)$") + -- Lua type() always returns lowercase, also make sure the match() succeeded + typeExpected = typeExpected and typeExpected:lower() + or error("bad function name '"..funcName.."' for type assertion") + + M[funcName] = function(value, extra_msg_or_nil) + if type(value) ~= typeExpected then + if type(value) == 'nil' then + fail_fmt(2, extra_msg_or_nil, 'expected: a %s value, actual: nil', + typeExpected, type(value), prettystrPairs(value)) + else + fail_fmt(2, extra_msg_or_nil, 'expected: a %s value, actual: type %s, value %s', + typeExpected, type(value), prettystrPairs(value)) + end + end + end +end + +--[[ +Add shortcuts for verifying type of a variable, without failure (luaunit v2 compatibility) +M.isXxx(value) -> returns true if type(value) conforms to "xxx" +]] +for _, typeExpected in ipairs( + {'Number', 'String', 'Table', 'Boolean', + 'Function', 'Userdata', 'Thread', 'Nil' } +) do + local typeExpectedLower = typeExpected:lower() + local isType = function(value) + return (type(value) == typeExpectedLower) + end + M['is'..typeExpected] = isType + M['is_'..typeExpectedLower] = isType +end + +--[[ +Add non-type assertion functions to the module table M. Each of these functions +takes a single parameter "value", and checks that its Lua type differs from the +expected string (derived from the function name): + +M.assertNotIsXxx(value) -> ensure that type(value) is not "xxx" +]] +for _, funcName in ipairs( + {'assertNotIsNumber', 'assertNotIsString', 'assertNotIsTable', 'assertNotIsBoolean', + 'assertNotIsFunction', 'assertNotIsUserdata', 'assertNotIsThread'} +) do + local typeUnexpected = funcName:match("^assertNotIs([A-Z]%a*)$") + -- Lua type() always returns lowercase, also make sure the match() succeeded + typeUnexpected = typeUnexpected and typeUnexpected:lower() + or error("bad function name '"..funcName.."' for type assertion") + + M[funcName] = function(value, extra_msg_or_nil) + if type(value) == typeUnexpected then + fail_fmt(2, extra_msg_or_nil, 'expected: not a %s type, actual: value %s', + typeUnexpected, prettystrPairs(value)) + end + end +end + +function M.assertIs(actual, expected, extra_msg_or_nil) + if actual ~= expected then + if not M.ORDER_ACTUAL_EXPECTED then + actual, expected = expected, actual + end + local old_print_table_ref_in_error_msg = M.PRINT_TABLE_REF_IN_ERROR_MSG + M.PRINT_TABLE_REF_IN_ERROR_MSG = true + expected, actual = prettystrPairs(expected, actual, '\n', '') + M.PRINT_TABLE_REF_IN_ERROR_MSG = old_print_table_ref_in_error_msg + fail_fmt(2, extra_msg_or_nil, 'expected and actual object should not be different\nExpected: %s\nReceived: %s', + expected, actual) + end +end + +function M.assertNotIs(actual, expected, extra_msg_or_nil) + if actual == expected then + local old_print_table_ref_in_error_msg = M.PRINT_TABLE_REF_IN_ERROR_MSG + M.PRINT_TABLE_REF_IN_ERROR_MSG = true + local s_expected + if not M.ORDER_ACTUAL_EXPECTED then + s_expected = prettystrPairs(actual) + else + s_expected = prettystrPairs(expected) + end + M.PRINT_TABLE_REF_IN_ERROR_MSG = old_print_table_ref_in_error_msg + fail_fmt(2, extra_msg_or_nil, 'expected and actual object should be different: %s', s_expected ) + end +end + + +------------------------------------------------------------------ +-- Scientific assertions +------------------------------------------------------------------ + + +function M.assertIsNaN(value, extra_msg_or_nil) + if type(value) ~= "number" or value == value then + failure("expected: NaN, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertNotIsNaN(value, extra_msg_or_nil) + if type(value) == "number" and value ~= value then + failure("expected: not NaN, actual: NaN", extra_msg_or_nil, 2) + end +end + +function M.assertIsInf(value, extra_msg_or_nil) + if type(value) ~= "number" or math.abs(value) ~= math.huge then + failure("expected: #Inf, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertIsPlusInf(value, extra_msg_or_nil) + if type(value) ~= "number" or value ~= math.huge then + failure("expected: #Inf, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertIsMinusInf(value, extra_msg_or_nil) + if type(value) ~= "number" or value ~= -math.huge then + failure("expected: -#Inf, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertNotIsPlusInf(value, extra_msg_or_nil) + if type(value) == "number" and value == math.huge then + failure("expected: not #Inf, actual: #Inf", extra_msg_or_nil, 2) + end +end + +function M.assertNotIsMinusInf(value, extra_msg_or_nil) + if type(value) == "number" and value == -math.huge then + failure("expected: not -#Inf, actual: -#Inf", extra_msg_or_nil, 2) + end +end + +function M.assertNotIsInf(value, extra_msg_or_nil) + if type(value) == "number" and math.abs(value) == math.huge then + failure("expected: not infinity, actual: " .. prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertIsPlusZero(value, extra_msg_or_nil) + if type(value) ~= 'number' or value ~= 0 then + failure("expected: +0.0, actual: " ..prettystr(value), extra_msg_or_nil, 2) + else if (1/value == -math.huge) then + -- more precise error diagnosis + failure("expected: +0.0, actual: -0.0", extra_msg_or_nil, 2) + else if (1/value ~= math.huge) then + -- strange, case should have already been covered + failure("expected: +0.0, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end + end + end +end + +function M.assertIsMinusZero(value, extra_msg_or_nil) + if type(value) ~= 'number' or value ~= 0 then + failure("expected: -0.0, actual: " ..prettystr(value), extra_msg_or_nil, 2) + else if (1/value == math.huge) then + -- more precise error diagnosis + failure("expected: -0.0, actual: +0.0", extra_msg_or_nil, 2) + else if (1/value ~= -math.huge) then + -- strange, case should have already been covered + failure("expected: -0.0, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end + end + end +end + +function M.assertNotIsPlusZero(value, extra_msg_or_nil) + if type(value) == 'number' and (1/value == math.huge) then + failure("expected: not +0.0, actual: +0.0", extra_msg_or_nil, 2) + end +end + +function M.assertNotIsMinusZero(value, extra_msg_or_nil) + if type(value) == 'number' and (1/value == -math.huge) then + failure("expected: not -0.0, actual: -0.0", extra_msg_or_nil, 2) + end +end + +function M.assertTableContains(t, expected, extra_msg_or_nil) + -- checks that table t contains the expected element + if table_findkeyof(t, expected) == nil then + t, expected = prettystrPairs(t, expected) + fail_fmt(2, extra_msg_or_nil, 'Table %s does NOT contain the expected element %s', + t, expected) + end +end + +function M.assertNotTableContains(t, expected, extra_msg_or_nil) + -- checks that table t doesn't contain the expected element + local k = table_findkeyof(t, expected) + if k ~= nil then + t, expected = prettystrPairs(t, expected) + fail_fmt(2, extra_msg_or_nil, 'Table %s DOES contain the unwanted element %s (at key %s)', + t, expected, prettystr(k)) + end +end + +---------------------------------------------------------------- +-- Compatibility layer +---------------------------------------------------------------- + +-- for compatibility with LuaUnit v2.x +function M.wrapFunctions() + -- In LuaUnit version <= 2.1 , this function was necessary to include + -- a test function inside the global test suite. Nowadays, the functions + -- are simply run directly as part of the test discovery process. + -- so just do nothing ! + io.stderr:write[[Use of WrapFunctions() is no longer needed. +Just prefix your test function names with "test" or "Test" and they +will be picked up and run by LuaUnit. +]] +end + +local list_of_funcs = { + -- { official function name , alias } + + -- general assertions + { 'assertEquals' , 'assert_equals' }, + { 'assertItemsEquals' , 'assert_items_equals' }, + { 'assertNotEquals' , 'assert_not_equals' }, + { 'assertAlmostEquals' , 'assert_almost_equals' }, + { 'assertNotAlmostEquals' , 'assert_not_almost_equals' }, + { 'assertEvalToTrue' , 'assert_eval_to_true' }, + { 'assertEvalToFalse' , 'assert_eval_to_false' }, + { 'assertStrContains' , 'assert_str_contains' }, + { 'assertStrIContains' , 'assert_str_icontains' }, + { 'assertNotStrContains' , 'assert_not_str_contains' }, + { 'assertNotStrIContains' , 'assert_not_str_icontains' }, + { 'assertStrMatches' , 'assert_str_matches' }, + { 'assertError' , 'assert_error' }, + { 'assertErrorMsgEquals' , 'assert_error_msg_equals' }, + { 'assertErrorMsgContains' , 'assert_error_msg_contains' }, + { 'assertErrorMsgMatches' , 'assert_error_msg_matches' }, + { 'assertErrorMsgContentEquals', 'assert_error_msg_content_equals' }, + { 'assertIs' , 'assert_is' }, + { 'assertNotIs' , 'assert_not_is' }, + { 'assertTableContains' , 'assert_table_contains' }, + { 'assertNotTableContains' , 'assert_not_table_contains' }, + { 'wrapFunctions' , 'WrapFunctions' }, + { 'wrapFunctions' , 'wrap_functions' }, + + -- type assertions: assertIsXXX -> assert_is_xxx + { 'assertIsNumber' , 'assert_is_number' }, + { 'assertIsString' , 'assert_is_string' }, + { 'assertIsTable' , 'assert_is_table' }, + { 'assertIsBoolean' , 'assert_is_boolean' }, + { 'assertIsNil' , 'assert_is_nil' }, + { 'assertIsTrue' , 'assert_is_true' }, + { 'assertIsFalse' , 'assert_is_false' }, + { 'assertIsNaN' , 'assert_is_nan' }, + { 'assertIsInf' , 'assert_is_inf' }, + { 'assertIsPlusInf' , 'assert_is_plus_inf' }, + { 'assertIsMinusInf' , 'assert_is_minus_inf' }, + { 'assertIsPlusZero' , 'assert_is_plus_zero' }, + { 'assertIsMinusZero' , 'assert_is_minus_zero' }, + { 'assertIsFunction' , 'assert_is_function' }, + { 'assertIsThread' , 'assert_is_thread' }, + { 'assertIsUserdata' , 'assert_is_userdata' }, + + -- type assertions: assertIsXXX -> assertXxx + { 'assertIsNumber' , 'assertNumber' }, + { 'assertIsString' , 'assertString' }, + { 'assertIsTable' , 'assertTable' }, + { 'assertIsBoolean' , 'assertBoolean' }, + { 'assertIsNil' , 'assertNil' }, + { 'assertIsTrue' , 'assertTrue' }, + { 'assertIsFalse' , 'assertFalse' }, + { 'assertIsNaN' , 'assertNaN' }, + { 'assertIsInf' , 'assertInf' }, + { 'assertIsPlusInf' , 'assertPlusInf' }, + { 'assertIsMinusInf' , 'assertMinusInf' }, + { 'assertIsPlusZero' , 'assertPlusZero' }, + { 'assertIsMinusZero' , 'assertMinusZero'}, + { 'assertIsFunction' , 'assertFunction' }, + { 'assertIsThread' , 'assertThread' }, + { 'assertIsUserdata' , 'assertUserdata' }, + + -- type assertions: assertIsXXX -> assert_xxx (luaunit v2 compat) + { 'assertIsNumber' , 'assert_number' }, + { 'assertIsString' , 'assert_string' }, + { 'assertIsTable' , 'assert_table' }, + { 'assertIsBoolean' , 'assert_boolean' }, + { 'assertIsNil' , 'assert_nil' }, + { 'assertIsTrue' , 'assert_true' }, + { 'assertIsFalse' , 'assert_false' }, + { 'assertIsNaN' , 'assert_nan' }, + { 'assertIsInf' , 'assert_inf' }, + { 'assertIsPlusInf' , 'assert_plus_inf' }, + { 'assertIsMinusInf' , 'assert_minus_inf' }, + { 'assertIsPlusZero' , 'assert_plus_zero' }, + { 'assertIsMinusZero' , 'assert_minus_zero' }, + { 'assertIsFunction' , 'assert_function' }, + { 'assertIsThread' , 'assert_thread' }, + { 'assertIsUserdata' , 'assert_userdata' }, + + -- type assertions: assertNotIsXXX -> assert_not_is_xxx + { 'assertNotIsNumber' , 'assert_not_is_number' }, + { 'assertNotIsString' , 'assert_not_is_string' }, + { 'assertNotIsTable' , 'assert_not_is_table' }, + { 'assertNotIsBoolean' , 'assert_not_is_boolean' }, + { 'assertNotIsNil' , 'assert_not_is_nil' }, + { 'assertNotIsTrue' , 'assert_not_is_true' }, + { 'assertNotIsFalse' , 'assert_not_is_false' }, + { 'assertNotIsNaN' , 'assert_not_is_nan' }, + { 'assertNotIsInf' , 'assert_not_is_inf' }, + { 'assertNotIsPlusInf' , 'assert_not_plus_inf' }, + { 'assertNotIsMinusInf' , 'assert_not_minus_inf' }, + { 'assertNotIsPlusZero' , 'assert_not_plus_zero' }, + { 'assertNotIsMinusZero' , 'assert_not_minus_zero' }, + { 'assertNotIsFunction' , 'assert_not_is_function' }, + { 'assertNotIsThread' , 'assert_not_is_thread' }, + { 'assertNotIsUserdata' , 'assert_not_is_userdata' }, + + -- type assertions: assertNotIsXXX -> assertNotXxx (luaunit v2 compat) + { 'assertNotIsNumber' , 'assertNotNumber' }, + { 'assertNotIsString' , 'assertNotString' }, + { 'assertNotIsTable' , 'assertNotTable' }, + { 'assertNotIsBoolean' , 'assertNotBoolean' }, + { 'assertNotIsNil' , 'assertNotNil' }, + { 'assertNotIsTrue' , 'assertNotTrue' }, + { 'assertNotIsFalse' , 'assertNotFalse' }, + { 'assertNotIsNaN' , 'assertNotNaN' }, + { 'assertNotIsInf' , 'assertNotInf' }, + { 'assertNotIsPlusInf' , 'assertNotPlusInf' }, + { 'assertNotIsMinusInf' , 'assertNotMinusInf' }, + { 'assertNotIsPlusZero' , 'assertNotPlusZero' }, + { 'assertNotIsMinusZero' , 'assertNotMinusZero' }, + { 'assertNotIsFunction' , 'assertNotFunction' }, + { 'assertNotIsThread' , 'assertNotThread' }, + { 'assertNotIsUserdata' , 'assertNotUserdata' }, + + -- type assertions: assertNotIsXXX -> assert_not_xxx + { 'assertNotIsNumber' , 'assert_not_number' }, + { 'assertNotIsString' , 'assert_not_string' }, + { 'assertNotIsTable' , 'assert_not_table' }, + { 'assertNotIsBoolean' , 'assert_not_boolean' }, + { 'assertNotIsNil' , 'assert_not_nil' }, + { 'assertNotIsTrue' , 'assert_not_true' }, + { 'assertNotIsFalse' , 'assert_not_false' }, + { 'assertNotIsNaN' , 'assert_not_nan' }, + { 'assertNotIsInf' , 'assert_not_inf' }, + { 'assertNotIsPlusInf' , 'assert_not_plus_inf' }, + { 'assertNotIsMinusInf' , 'assert_not_minus_inf' }, + { 'assertNotIsPlusZero' , 'assert_not_plus_zero' }, + { 'assertNotIsMinusZero' , 'assert_not_minus_zero' }, + { 'assertNotIsFunction' , 'assert_not_function' }, + { 'assertNotIsThread' , 'assert_not_thread' }, + { 'assertNotIsUserdata' , 'assert_not_userdata' }, + + -- all assertions with Coroutine duplicate Thread assertions + { 'assertIsThread' , 'assertIsCoroutine' }, + { 'assertIsThread' , 'assertCoroutine' }, + { 'assertIsThread' , 'assert_is_coroutine' }, + { 'assertIsThread' , 'assert_coroutine' }, + { 'assertNotIsThread' , 'assertNotIsCoroutine' }, + { 'assertNotIsThread' , 'assertNotCoroutine' }, + { 'assertNotIsThread' , 'assert_not_is_coroutine' }, + { 'assertNotIsThread' , 'assert_not_coroutine' }, +} + +-- Create all aliases in M +for _,v in ipairs( list_of_funcs ) do + local funcname, alias = v[1], v[2] + M[alias] = M[funcname] + + if EXPORT_ASSERT_TO_GLOBALS then + _G[funcname] = M[funcname] + _G[alias] = M[funcname] + end +end + +---------------------------------------------------------------- +-- +-- Outputters +-- +---------------------------------------------------------------- + +-- A common "base" class for outputters +-- For concepts involved (class inheritance) see http://www.lua.org/pil/16.2.html + +local genericOutput = { __class__ = 'genericOutput' } -- class +local genericOutput_MT = { __index = genericOutput } -- metatable +M.genericOutput = genericOutput -- publish, so that custom classes may derive from it + +function genericOutput.new(runner, default_verbosity) + -- runner is the "parent" object controlling the output, usually a LuaUnit instance + local t = { runner = runner } + if runner then + t.result = runner.result + t.verbosity = runner.verbosity or default_verbosity + t.fname = runner.fname + else + t.verbosity = default_verbosity + end + return setmetatable( t, genericOutput_MT) +end + +-- abstract ("empty") methods +function genericOutput:startSuite() + -- Called once, when the suite is started +end + +function genericOutput:startClass(className) + -- Called each time a new test class is started +end + +function genericOutput:startTest(testName) + -- called each time a new test is started, right before the setUp() + -- the current test status node is already created and available in: self.result.currentNode +end + +function genericOutput:updateStatus(node) + -- called with status failed or error as soon as the error/failure is encountered + -- this method is NOT called for a successful test because a test is marked as successful by default + -- and does not need to be updated +end + +function genericOutput:endTest(node) + -- called when the test is finished, after the tearDown() method +end + +function genericOutput:endClass() + -- called when executing the class is finished, before moving on to the next class of at the end of the test execution +end + +function genericOutput:endSuite() + -- called at the end of the test suite execution +end + + +---------------------------------------------------------------- +-- class TapOutput +---------------------------------------------------------------- + +local TapOutput = genericOutput.new() -- derived class +local TapOutput_MT = { __index = TapOutput } -- metatable +TapOutput.__class__ = 'TapOutput' + + -- For a good reference for TAP format, check: http://testanything.org/tap-specification.html + + function TapOutput.new(runner) + local t = genericOutput.new(runner, M.VERBOSITY_LOW) + return setmetatable( t, TapOutput_MT) + end + function TapOutput:startSuite() + print("1.."..self.result.selectedCount) + print('# Started on '..self.result.startDate) + end + function TapOutput:startClass(className) + if className ~= '[TestFunctions]' then + print('# Starting class: '..className) + end + end + + function TapOutput:updateStatus( node ) + if node:isSkipped() then + io.stdout:write("ok ", self.result.currentTestNumber, "\t# SKIP ", node.msg, "\n" ) + return + end + + io.stdout:write("not ok ", self.result.currentTestNumber, "\t", node.testName, "\n") + if self.verbosity > M.VERBOSITY_LOW then + print( prefixString( '# ', node.msg ) ) + end + if (node:isFailure() or node:isError()) and self.verbosity > M.VERBOSITY_DEFAULT then + print( prefixString( '# ', node.stackTrace ) ) + end + end + + function TapOutput:endTest( node ) + if node:isSuccess() then + io.stdout:write("ok ", self.result.currentTestNumber, "\t", node.testName, "\n") + end + end + + function TapOutput:endSuite() + print( '# '..M.LuaUnit.statusLine( self.result ) ) + return self.result.notSuccessCount + end + + +-- class TapOutput end + +---------------------------------------------------------------- +-- class JUnitOutput +---------------------------------------------------------------- + +-- See directory junitxml for more information about the junit format +local JUnitOutput = genericOutput.new() -- derived class +local JUnitOutput_MT = { __index = JUnitOutput } -- metatable +JUnitOutput.__class__ = 'JUnitOutput' + + function JUnitOutput.new(runner) + local t = genericOutput.new(runner, M.VERBOSITY_LOW) + t.testList = {} + return setmetatable( t, JUnitOutput_MT ) + end + + function JUnitOutput:startSuite() + -- open xml file early to deal with errors + if self.fname == nil then + error('With Junit, an output filename must be supplied with --name!') + end + if string.sub(self.fname,-4) ~= '.xml' then + self.fname = self.fname..'.xml' + end + self.fd = io.open(self.fname, "w") + if self.fd == nil then + error("Could not open file for writing: "..self.fname) + end + + print('# XML output to '..self.fname) + print('# Started on '..self.result.startDate) + end + function JUnitOutput:startClass(className) + if className ~= '[TestFunctions]' then + print('# Starting class: '..className) + end + end + function JUnitOutput:startTest(testName) + print('# Starting test: '..testName) + end + + function JUnitOutput:updateStatus( node ) + if node:isFailure() then + print( '# Failure: ' .. prefixString( '# ', node.msg ):sub(4, nil) ) + -- print('# ' .. node.stackTrace) + elseif node:isError() then + print( '# Error: ' .. prefixString( '# ' , node.msg ):sub(4, nil) ) + -- print('# ' .. node.stackTrace) + end + end + + function JUnitOutput:endSuite() + print( '# '..M.LuaUnit.statusLine(self.result)) + + -- XML file writing + self.fd:write('\n') + self.fd:write('\n') + self.fd:write(string.format( + ' \n', + self.result.runCount, self.result.startIsodate, self.result.duration, self.result.errorCount, self.result.failureCount, self.result.skippedCount )) + self.fd:write(" \n") + self.fd:write(string.format(' \n', _VERSION ) ) + self.fd:write(string.format(' \n', M.VERSION) ) + -- XXX please include system name and version if possible + self.fd:write(" \n") + + for i,node in ipairs(self.result.allTests) do + self.fd:write(string.format(' \n', + node.className, node.testName, node.duration ) ) + if node:isNotSuccess() then + self.fd:write(node:statusXML()) + end + self.fd:write(' \n') + end + + -- Next two lines are needed to validate junit ANT xsd, but really not useful in general: + self.fd:write(' \n') + self.fd:write(' \n') + + self.fd:write(' \n') + self.fd:write('\n') + self.fd:close() + return self.result.notSuccessCount + end + + +-- class TapOutput end + +---------------------------------------------------------------- +-- class TextOutput +---------------------------------------------------------------- + +--[[ Example of other unit-tests suite text output + +-- Python Non verbose: + +For each test: . or F or E + +If some failed tests: + ============== + ERROR / FAILURE: TestName (testfile.testclass) + --------- + Stack trace + + +then -------------- +then "Ran x tests in 0.000s" +then OK or FAILED (failures=1, error=1) + +-- Python Verbose: +testname (filename.classname) ... ok +testname (filename.classname) ... FAIL +testname (filename.classname) ... ERROR + +then -------------- +then "Ran x tests in 0.000s" +then OK or FAILED (failures=1, error=1) + +-- Ruby: +Started + . + Finished in 0.002695 seconds. + + 1 tests, 2 assertions, 0 failures, 0 errors + +-- Ruby: +>> ruby tc_simple_number2.rb +Loaded suite tc_simple_number2 +Started +F.. +Finished in 0.038617 seconds. + + 1) Failure: +test_failure(TestSimpleNumber) [tc_simple_number2.rb:16]: +Adding doesn't work. +<3> expected but was +<4>. + +3 tests, 4 assertions, 1 failures, 0 errors + +-- Java Junit +.......F. +Time: 0,003 +There was 1 failure: +1) testCapacity(junit.samples.VectorTest)junit.framework.AssertionFailedError + at junit.samples.VectorTest.testCapacity(VectorTest.java:87) + at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) + at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + +FAILURES!!! +Tests run: 8, Failures: 1, Errors: 0 + + +-- Maven + +# mvn test +------------------------------------------------------- + T E S T S +------------------------------------------------------- +Running math.AdditionTest +Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: +0.03 sec <<< FAILURE! + +Results : + +Failed tests: + testLireSymbole(math.AdditionTest) + +Tests run: 2, Failures: 1, Errors: 0, Skipped: 0 + + +-- LuaUnit +---- non verbose +* display . or F or E when running tests +---- verbose +* display test name + ok/fail +---- +* blank line +* number) ERROR or FAILURE: TestName + Stack trace +* blank line +* number) ERROR or FAILURE: TestName + Stack trace + +then -------------- +then "Ran x tests in 0.000s (%d not selected, %d skipped)" +then OK or FAILED (failures=1, error=1) + + +]] + +local TextOutput = genericOutput.new() -- derived class +local TextOutput_MT = { __index = TextOutput } -- metatable +TextOutput.__class__ = 'TextOutput' + + function TextOutput.new(runner) + local t = genericOutput.new(runner, M.VERBOSITY_DEFAULT) + t.errorList = {} + return setmetatable( t, TextOutput_MT ) + end + + function TextOutput:startSuite() + if self.verbosity > M.VERBOSITY_DEFAULT then + print( 'Started on '.. self.result.startDate ) + end + end + + function TextOutput:startTest(testName) + if self.verbosity > M.VERBOSITY_DEFAULT then + io.stdout:write( " ", self.result.currentNode.testName, " ... " ) + end + end + + function TextOutput:endTest( node ) + if node:isSuccess() then + if self.verbosity > M.VERBOSITY_DEFAULT then + io.stdout:write("Ok\n") + else + io.stdout:write(".") + io.stdout:flush() + end + else + if self.verbosity > M.VERBOSITY_DEFAULT then + print( node.status ) + print( node.msg ) + --[[ + -- find out when to do this: + if self.verbosity > M.VERBOSITY_DEFAULT then + print( node.stackTrace ) + end + ]] + else + -- write only the first character of status E, F or S + io.stdout:write(string.sub(node.status, 1, 1)) + io.stdout:flush() + end + end + end + + function TextOutput:displayOneFailedTest( index, fail ) + print(index..") "..fail.testName ) + print( fail.msg ) + print( fail.stackTrace ) + print() + end + + function TextOutput:displayErroredTests() + if #self.result.errorTests ~= 0 then + print("Tests with errors:") + print("------------------") + for i, v in ipairs(self.result.errorTests) do + self:displayOneFailedTest(i, v) + end + end + end + + function TextOutput:displayFailedTests() + if #self.result.failedTests ~= 0 then + print("Failed tests:") + print("-------------") + for i, v in ipairs(self.result.failedTests) do + self:displayOneFailedTest(i, v) + end + end + end + + function TextOutput:endSuite() + if self.verbosity > M.VERBOSITY_DEFAULT then + print("=========================================================") + else + print() + end + self:displayErroredTests() + self:displayFailedTests() + print( M.LuaUnit.statusLine( self.result ) ) + if self.result.notSuccessCount == 0 then + print('OK') + end + end + +-- class TextOutput end + + +---------------------------------------------------------------- +-- class NilOutput +---------------------------------------------------------------- + +local function nopCallable() + --print(42) + return nopCallable +end + +local NilOutput = { __class__ = 'NilOuptut' } -- class +local NilOutput_MT = { __index = nopCallable } -- metatable + +function NilOutput.new(runner) + return setmetatable( { __class__ = 'NilOutput' }, NilOutput_MT ) +end + +---------------------------------------------------------------- +-- +-- class LuaUnit +-- +---------------------------------------------------------------- + +M.LuaUnit = { + outputType = TextOutput, + verbosity = M.VERBOSITY_DEFAULT, + __class__ = 'LuaUnit', + instances = {} +} +local LuaUnit_MT = { __index = M.LuaUnit } + +if EXPORT_ASSERT_TO_GLOBALS then + LuaUnit = M.LuaUnit +end + + function M.LuaUnit.new() + local newInstance = setmetatable( {}, LuaUnit_MT ) + return newInstance + end + + -----------------[[ Utility methods ]]--------------------- + + function M.LuaUnit.asFunction(aObject) + -- return "aObject" if it is a function, and nil otherwise + if 'function' == type(aObject) then + return aObject + end + end + + function M.LuaUnit.splitClassMethod(someName) + --[[ + Return a pair of className, methodName strings for a name in the form + "class.method". If no class part (or separator) is found, will return + nil, someName instead (the latter being unchanged). + + This convention thus also replaces the older isClassMethod() test: + You just have to check for a non-nil className (return) value. + ]] + local separator = string.find(someName, '.', 1, true) + if separator then + return someName:sub(1, separator - 1), someName:sub(separator + 1) + end + return nil, someName + end + + function M.LuaUnit.isMethodTestName( s ) + -- return true is the name matches the name of a test method + -- default rule is that is starts with 'Test' or with 'test' + return string.sub(s, 1, 4):lower() == 'test' + end + + function M.LuaUnit.isTestName( s ) + -- return true is the name matches the name of a test + -- default rule is that is starts with 'Test' or with 'test' + return string.sub(s, 1, 4):lower() == 'test' + end + + function M.LuaUnit.collectTests() + -- return a list of all test names in the global namespace + -- that match LuaUnit.isTestName + + local testNames = {} + for k, _ in pairs(_G) do + if type(k) == "string" and M.LuaUnit.isTestName( k ) then + table.insert( testNames , k ) + end + end + table.sort( testNames ) + return testNames + end + + function M.LuaUnit.parseCmdLine( cmdLine ) + -- parse the command line + -- Supported command line parameters: + -- --verbose, -v: increase verbosity + -- --quiet, -q: silence output + -- --error, -e: treat errors as fatal (quit program) + -- --output, -o, + name: select output type + -- --pattern, -p, + pattern: run test matching pattern, may be repeated + -- --exclude, -x, + pattern: run test not matching pattern, may be repeated + -- --shuffle, -s, : shuffle tests before reunning them + -- --name, -n, + fname: name of output file for junit, default to stdout + -- --repeat, -r, + num: number of times to execute each test + -- [testnames, ...]: run selected test names + -- + -- Returns a table with the following fields: + -- verbosity: nil, M.VERBOSITY_DEFAULT, M.VERBOSITY_QUIET, M.VERBOSITY_VERBOSE + -- output: nil, 'tap', 'junit', 'text', 'nil' + -- testNames: nil or a list of test names to run + -- exeRepeat: num or 1 + -- pattern: nil or a list of patterns + -- exclude: nil or a list of patterns + + local result, state = {}, nil + local SET_OUTPUT = 1 + local SET_PATTERN = 2 + local SET_EXCLUDE = 3 + local SET_FNAME = 4 + local SET_REPEAT = 5 + + if cmdLine == nil then + return result + end + + local function parseOption( option ) + if option == '--help' or option == '-h' then + result['help'] = true + return + elseif option == '--version' then + result['version'] = true + return + elseif option == '--verbose' or option == '-v' then + result['verbosity'] = M.VERBOSITY_VERBOSE + return + elseif option == '--quiet' or option == '-q' then + result['verbosity'] = M.VERBOSITY_QUIET + return + elseif option == '--error' or option == '-e' then + result['quitOnError'] = true + return + elseif option == '--failure' or option == '-f' then + result['quitOnFailure'] = true + return + elseif option == '--shuffle' or option == '-s' then + result['shuffle'] = true + return + elseif option == '--output' or option == '-o' then + state = SET_OUTPUT + return state + elseif option == '--name' or option == '-n' then + state = SET_FNAME + return state + elseif option == '--repeat' or option == '-r' then + state = SET_REPEAT + return state + elseif option == '--pattern' or option == '-p' then + state = SET_PATTERN + return state + elseif option == '--exclude' or option == '-x' then + state = SET_EXCLUDE + return state + end + error('Unknown option: '..option,3) + end + + local function setArg( cmdArg, state ) + if state == SET_OUTPUT then + result['output'] = cmdArg + return + elseif state == SET_FNAME then + result['fname'] = cmdArg + return + elseif state == SET_REPEAT then + result['exeRepeat'] = tonumber(cmdArg) + or error('Malformed -r argument: '..cmdArg) + return + elseif state == SET_PATTERN then + if result['pattern'] then + table.insert( result['pattern'], cmdArg ) + else + result['pattern'] = { cmdArg } + end + return + elseif state == SET_EXCLUDE then + local notArg = '!'..cmdArg + if result['pattern'] then + table.insert( result['pattern'], notArg ) + else + result['pattern'] = { notArg } + end + return + end + error('Unknown parse state: '.. state) + end + + + for i, cmdArg in ipairs(cmdLine) do + if state ~= nil then + setArg( cmdArg, state, result ) + state = nil + else + if cmdArg:sub(1,1) == '-' then + state = parseOption( cmdArg ) + else + if result['testNames'] then + table.insert( result['testNames'], cmdArg ) + else + result['testNames'] = { cmdArg } + end + end + end + end + + if result['help'] then + M.LuaUnit.help() + end + + if result['version'] then + M.LuaUnit.version() + end + + if state ~= nil then + error('Missing argument after '..cmdLine[ #cmdLine ],2 ) + end + + return result + end + + function M.LuaUnit.help() + print(M.USAGE) + os.exit(0) + end + + function M.LuaUnit.version() + print('LuaUnit v'..M.VERSION..' by Philippe Fremy ') + os.exit(0) + end + +---------------------------------------------------------------- +-- class NodeStatus +---------------------------------------------------------------- + + local NodeStatus = { __class__ = 'NodeStatus' } -- class + local NodeStatus_MT = { __index = NodeStatus } -- metatable + M.NodeStatus = NodeStatus + + -- values of status + NodeStatus.SUCCESS = 'SUCCESS' + NodeStatus.SKIP = 'SKIP' + NodeStatus.FAIL = 'FAIL' + NodeStatus.ERROR = 'ERROR' + + function NodeStatus.new( number, testName, className ) + -- default constructor, test are PASS by default + local t = { number = number, testName = testName, className = className } + setmetatable( t, NodeStatus_MT ) + t:success() + return t + end + + function NodeStatus:success() + self.status = self.SUCCESS + -- useless because lua does this for us, but it helps me remembering the relevant field names + self.msg = nil + self.stackTrace = nil + end + + function NodeStatus:skip(msg) + self.status = self.SKIP + self.msg = msg + self.stackTrace = nil + end + + function NodeStatus:fail(msg, stackTrace) + self.status = self.FAIL + self.msg = msg + self.stackTrace = stackTrace + end + + function NodeStatus:error(msg, stackTrace) + self.status = self.ERROR + self.msg = msg + self.stackTrace = stackTrace + end + + function NodeStatus:isSuccess() + return self.status == NodeStatus.SUCCESS + end + + function NodeStatus:isNotSuccess() + -- Return true if node is either failure or error or skip + return (self.status == NodeStatus.FAIL or self.status == NodeStatus.ERROR or self.status == NodeStatus.SKIP) + end + + function NodeStatus:isSkipped() + return self.status == NodeStatus.SKIP + end + + function NodeStatus:isFailure() + return self.status == NodeStatus.FAIL + end + + function NodeStatus:isError() + return self.status == NodeStatus.ERROR + end + + function NodeStatus:statusXML() + if self:isError() then + return table.concat( + {' \n', + ' \n'}) + elseif self:isFailure() then + return table.concat( + {' \n', + ' \n'}) + elseif self:isSkipped() then + return table.concat({' ', xmlEscape(self.msg),'\n' } ) + end + return ' \n' -- (not XSD-compliant! normally shouldn't get here) + end + + --------------[[ Output methods ]]------------------------- + + local function conditional_plural(number, singular) + -- returns a grammatically well-formed string "%d " + local suffix = '' + if number ~= 1 then -- use plural + suffix = (singular:sub(-2) == 'ss') and 'es' or 's' + end + return string.format('%d %s%s', number, singular, suffix) + end + + function M.LuaUnit.statusLine(result) + -- return status line string according to results + local s = { + string.format('Ran %d tests in %0.3f seconds', + result.runCount, result.duration), + conditional_plural(result.successCount, 'success'), + } + if result.notSuccessCount > 0 then + if result.failureCount > 0 then + table.insert(s, conditional_plural(result.failureCount, 'failure')) + end + if result.errorCount > 0 then + table.insert(s, conditional_plural(result.errorCount, 'error')) + end + else + table.insert(s, '0 failures') + end + if result.skippedCount > 0 then + table.insert(s, string.format("%d skipped", result.skippedCount)) + end + if result.nonSelectedCount > 0 then + table.insert(s, string.format("%d non-selected", result.nonSelectedCount)) + end + return table.concat(s, ', ') + end + + function M.LuaUnit:startSuite(selectedCount, nonSelectedCount) + self.result = { + selectedCount = selectedCount, + nonSelectedCount = nonSelectedCount, + successCount = 0, + runCount = 0, + currentTestNumber = 0, + currentClassName = "", + currentNode = nil, + suiteStarted = true, + startTime = os.clock(), + startDate = os.date(os.getenv('LUAUNIT_DATEFMT')), + startIsodate = os.date('%Y-%m-%dT%H:%M:%S'), + patternIncludeFilter = self.patternIncludeFilter, + + -- list of test node status + allTests = {}, + failedTests = {}, + errorTests = {}, + skippedTests = {}, + + failureCount = 0, + errorCount = 0, + notSuccessCount = 0, + skippedCount = 0, + } + + self.outputType = self.outputType or TextOutput + self.output = self.outputType.new(self) + self.output:startSuite() + end + + function M.LuaUnit:startClass( className, classInstance ) + self.result.currentClassName = className + self.output:startClass( className ) + self:setupClass( className, classInstance ) + end + + function M.LuaUnit:startTest( testName ) + self.result.currentTestNumber = self.result.currentTestNumber + 1 + self.result.runCount = self.result.runCount + 1 + self.result.currentNode = NodeStatus.new( + self.result.currentTestNumber, + testName, + self.result.currentClassName + ) + self.result.currentNode.startTime = os.clock() + table.insert( self.result.allTests, self.result.currentNode ) + self.output:startTest( testName ) + end + + function M.LuaUnit:updateStatus( err ) + -- "err" is expected to be a table / result from protectedCall() + if err.status == NodeStatus.SUCCESS then + return + end + + local node = self.result.currentNode + + --[[ As a first approach, we will report only one error or one failure for one test. + + However, we can have the case where the test is in failure, and the teardown is in error. + In such case, it's a good idea to report both a failure and an error in the test suite. This is + what Python unittest does for example. However, it mixes up counts so need to be handled carefully: for + example, there could be more (failures + errors) count that tests. What happens to the current node ? + + We will do this more intelligent version later. + ]] + + -- if the node is already in failure/error, just don't report the new error (see above) + if node.status ~= NodeStatus.SUCCESS then + return + end + + if err.status == NodeStatus.FAIL then + node:fail( err.msg, err.trace ) + table.insert( self.result.failedTests, node ) + elseif err.status == NodeStatus.ERROR then + node:error( err.msg, err.trace ) + table.insert( self.result.errorTests, node ) + elseif err.status == NodeStatus.SKIP then + node:skip( err.msg ) + table.insert( self.result.skippedTests, node ) + else + error('No such status: ' .. prettystr(err.status)) + end + + self.output:updateStatus( node ) + end + + function M.LuaUnit:endTest() + local node = self.result.currentNode + -- print( 'endTest() '..prettystr(node)) + -- print( 'endTest() '..prettystr(node:isNotSuccess())) + node.duration = os.clock() - node.startTime + node.startTime = nil + self.output:endTest( node ) + + if node:isSuccess() then + self.result.successCount = self.result.successCount + 1 + elseif node:isError() then + if self.quitOnError or self.quitOnFailure then + -- Runtime error - abort test execution as requested by + -- "--error" option. This is done by setting a special + -- flag that gets handled in internalRunSuiteByInstances(). + print("\nERROR during LuaUnit test execution:\n" .. node.msg) + self.result.aborted = true + end + elseif node:isFailure() then + if self.quitOnFailure then + -- Failure - abort test execution as requested by + -- "--failure" option. This is done by setting a special + -- flag that gets handled in internalRunSuiteByInstances(). + print("\nFailure during LuaUnit test execution:\n" .. node.msg) + self.result.aborted = true + end + elseif node:isSkipped() then + self.result.runCount = self.result.runCount - 1 + else + error('No such node status: ' .. prettystr(node.status)) + end + self.result.currentNode = nil + end + + function M.LuaUnit:endClass() + self:teardownClass( self.lastClassName, self.lastClassInstance ) + self.output:endClass() + end + + function M.LuaUnit:endSuite() + if self.result.suiteStarted == false then + error('LuaUnit:endSuite() -- suite was already ended' ) + end + self.result.duration = os.clock()-self.result.startTime + self.result.suiteStarted = false + + -- Expose test counts for outputter's endSuite(). This could be managed + -- internally instead by using the length of the lists of failed tests + -- but unit tests rely on these fields being present. + self.result.failureCount = #self.result.failedTests + self.result.errorCount = #self.result.errorTests + self.result.notSuccessCount = self.result.failureCount + self.result.errorCount + self.result.skippedCount = #self.result.skippedTests + + self.output:endSuite() + end + + function M.LuaUnit:setOutputType(outputType, fname) + -- Configures LuaUnit runner output + -- outputType is one of: NIL, TAP, JUNIT, TEXT + -- when outputType is junit, the additional argument fname is used to set the name of junit output file + -- for other formats, fname is ignored + if outputType:upper() == "NIL" then + self.outputType = NilOutput + return + end + if outputType:upper() == "TAP" then + self.outputType = TapOutput + return + end + if outputType:upper() == "JUNIT" then + self.outputType = JUnitOutput + if fname then + self.fname = fname + end + return + end + if outputType:upper() == "TEXT" then + self.outputType = TextOutput + return + end + error( 'No such format: '..outputType,2) + end + + --------------[[ Runner ]]----------------- + + function M.LuaUnit:protectedCall(classInstance, methodInstance, prettyFuncName) + -- if classInstance is nil, this is just a function call + -- else, it's method of a class being called. + + local function err_handler(e) + -- transform error into a table, adding the traceback information + return { + status = NodeStatus.ERROR, + msg = e, + trace = string.sub(debug.traceback("", 1), 2) + } + end + + local ok, err + if classInstance then + -- stupid Lua < 5.2 does not allow xpcall with arguments so let's use a workaround + ok, err = xpcall( function () methodInstance(classInstance) end, err_handler ) + else + ok, err = xpcall( function () methodInstance() end, err_handler ) + end + if ok then + return {status = NodeStatus.SUCCESS} + end + -- print('ok="'..prettystr(ok)..'" err="'..prettystr(err)..'"') + + local iter_msg + iter_msg = self.exeRepeat and 'iteration '..self.currentCount + + err.msg, err.status = M.adjust_err_msg_with_iter( err.msg, iter_msg ) + + if err.status == NodeStatus.SUCCESS or err.status == NodeStatus.SKIP then + err.trace = nil + return err + end + + -- reformat / improve the stack trace + if prettyFuncName then -- we do have the real method name + err.trace = err.trace:gsub("in (%a+) 'methodInstance'", "in %1 '"..prettyFuncName.."'") + end + if STRIP_LUAUNIT_FROM_STACKTRACE then + err.trace = stripLuaunitTrace2(err.trace, err.msg) + end + + return err -- return the error "object" (table) + end + + + function M.LuaUnit:execOneFunction(className, methodName, classInstance, methodInstance) + -- When executing a test function, className and classInstance must be nil + -- When executing a class method, all parameters must be set + + if type(methodInstance) ~= 'function' then + self:unregisterSuite() + error( tostring(methodName)..' must be a function, not '..type(methodInstance)) + end + + local prettyFuncName + if className == nil then + className = '[TestFunctions]' + prettyFuncName = methodName + else + prettyFuncName = className..'.'..methodName + end + + if self.lastClassName ~= className then + if self.lastClassName ~= nil then + self:endClass() + end + self:startClass( className, classInstance ) + self.lastClassName = className + self.lastClassInstance = classInstance + end + + self:startTest(prettyFuncName) + + local node = self.result.currentNode + for iter_n = 1, self.exeRepeat or 1 do + if node:isNotSuccess() then + break + end + self.currentCount = iter_n + + -- run setUp first (if any) + if classInstance then + local func = self.asFunction( classInstance.setUp ) or + self.asFunction( classInstance.Setup ) or + self.asFunction( classInstance.setup ) or + self.asFunction( classInstance.SetUp ) + if func then + self:updateStatus(self:protectedCall(classInstance, func, className..'.setUp')) + end + end + + -- run testMethod() + if node:isSuccess() then + self:updateStatus(self:protectedCall(classInstance, methodInstance, prettyFuncName)) + end + + -- lastly, run tearDown (if any) + if classInstance then + local func = self.asFunction( classInstance.tearDown ) or + self.asFunction( classInstance.TearDown ) or + self.asFunction( classInstance.teardown ) or + self.asFunction( classInstance.Teardown ) + if func then + self:updateStatus(self:protectedCall(classInstance, func, className..'.tearDown')) + end + end + end + + self:endTest() + end + + function M.LuaUnit.expandOneClass( result, className, classInstance ) + --[[ + Input: a list of { name, instance }, a class name, a class instance + Ouptut: modify result to add all test method instance in the form: + { className.methodName, classInstance } + ]] + for methodName, methodInstance in sortedPairs(classInstance) do + if M.LuaUnit.asFunction(methodInstance) and M.LuaUnit.isMethodTestName( methodName ) then + table.insert( result, { className..'.'..methodName, classInstance } ) + end + end + end + + function M.LuaUnit.expandClasses( listOfNameAndInst ) + --[[ + -- expand all classes (provided as {className, classInstance}) to a list of {className.methodName, classInstance} + -- functions and methods remain untouched + + Input: a list of { name, instance } + + Output: + * { function name, function instance } : do nothing + * { class.method name, class instance }: do nothing + * { class name, class instance } : add all method names in the form of (className.methodName, classInstance) + ]] + local result = {} + + for i,v in ipairs( listOfNameAndInst ) do + local name, instance = v[1], v[2] + if M.LuaUnit.asFunction(instance) then + table.insert( result, { name, instance } ) + else + if type(instance) ~= 'table' then + error( 'Instance must be a table or a function, not a '..type(instance)..' with value '..prettystr(instance)) + end + local className, methodName = M.LuaUnit.splitClassMethod( name ) + if className then + local methodInstance = instance[methodName] + if methodInstance == nil then + error( "Could not find method in class "..tostring(className).." for method "..tostring(methodName) ) + end + table.insert( result, { name, instance } ) + else + M.LuaUnit.expandOneClass( result, name, instance ) + end + end + end + + return result + end + + function M.LuaUnit.applyPatternFilter( patternIncFilter, listOfNameAndInst ) + local included, excluded = {}, {} + for i, v in ipairs( listOfNameAndInst ) do + -- local name, instance = v[1], v[2] + if patternFilter( patternIncFilter, v[1] ) then + table.insert( included, v ) + else + table.insert( excluded, v ) + end + end + return included, excluded + end + + local function getKeyInListWithGlobalFallback( key, listOfNameAndInst ) + local result = nil + for i,v in ipairs( listOfNameAndInst ) do + if(listOfNameAndInst[i][1] == key) then + result = listOfNameAndInst[i][2] + break + end + end + if(not M.LuaUnit.asFunction( result ) ) then + result = _G[key] + end + return result + end + + function M.LuaUnit:setupSuite( listOfNameAndInst ) + local setupSuite = getKeyInListWithGlobalFallback("setupSuite", listOfNameAndInst) + if self.asFunction( setupSuite ) then + self:updateStatus( self:protectedCall( nil, setupSuite, 'setupSuite' ) ) + end + end + + function M.LuaUnit:teardownSuite(listOfNameAndInst) + local teardownSuite = getKeyInListWithGlobalFallback("teardownSuite", listOfNameAndInst) + if self.asFunction( teardownSuite ) then + self:updateStatus( self:protectedCall( nil, teardownSuite, 'teardownSuite') ) + end + end + + function M.LuaUnit:setupClass( className, instance ) + if type( instance ) == 'table' and self.asFunction( instance.setupClass ) then + self:updateStatus( self:protectedCall( instance, instance.setupClass, className..'.setupClass' ) ) + end + end + + function M.LuaUnit:teardownClass( className, instance ) + if type( instance ) == 'table' and self.asFunction( instance.teardownClass ) then + self:updateStatus( self:protectedCall( instance, instance.teardownClass, className..'.teardownClass' ) ) + end + end + + function M.LuaUnit:internalRunSuiteByInstances( listOfNameAndInst ) + --[[ Run an explicit list of tests. Each item of the list must be one of: + * { function name, function instance } + * { class name, class instance } + * { class.method name, class instance } + + This function is internal to LuaUnit. The official API to perform this action is runSuiteByInstances() + ]] + + local expandedList = self.expandClasses( listOfNameAndInst ) + if self.shuffle then + randomizeTable( expandedList ) + end + local filteredList, filteredOutList = self.applyPatternFilter( + self.patternIncludeFilter, expandedList ) + + self:startSuite( #filteredList, #filteredOutList ) + self:setupSuite( listOfNameAndInst ) + + for i,v in ipairs( filteredList ) do + local name, instance = v[1], v[2] + if M.LuaUnit.asFunction(instance) then + self:execOneFunction( nil, name, nil, instance ) + else + -- expandClasses() should have already taken care of sanitizing the input + assert( type(instance) == 'table' ) + local className, methodName = M.LuaUnit.splitClassMethod( name ) + assert( className ~= nil ) + local methodInstance = instance[methodName] + assert(methodInstance ~= nil) + self:execOneFunction( className, methodName, instance, methodInstance ) + end + if self.result.aborted then + break -- "--error" or "--failure" option triggered + end + end + + if self.lastClassName ~= nil then + self:endClass() + end + + self:teardownSuite( listOfNameAndInst ) + self:endSuite() + + if self.result.aborted then + print("LuaUnit ABORTED (as requested by --error or --failure option)") + self:unregisterSuite() + os.exit(-2) + end + end + + function M.LuaUnit:internalRunSuiteByNames( listOfName ) + --[[ Run LuaUnit with a list of generic names, coming either from command-line or from global + namespace analysis. Convert the list into a list of (name, valid instances (table or function)) + and calls internalRunSuiteByInstances. + ]] + + local instanceName, instance + local listOfNameAndInst = {} + + for i,name in ipairs( listOfName ) do + local className, methodName = M.LuaUnit.splitClassMethod( name ) + if className then + instanceName = className + instance = _G[instanceName] + + if instance == nil then + self:unregisterSuite() + error( "No such name in global space: "..instanceName ) + end + + if type(instance) ~= 'table' then + self:unregisterSuite() + error( 'Instance of '..instanceName..' must be a table, not '..type(instance)) + end + + local methodInstance = instance[methodName] + if methodInstance == nil then + self:unregisterSuite() + error( "Could not find method in class "..tostring(className).." for method "..tostring(methodName) ) + end + + else + -- for functions and classes + instanceName = name + instance = _G[instanceName] + end + + if instance == nil then + self:unregisterSuite() + error( "No such name in global space: "..instanceName ) + end + + if (type(instance) ~= 'table' and type(instance) ~= 'function') then + self:unregisterSuite() + error( 'Name must match a function or a table: '..instanceName ) + end + + table.insert( listOfNameAndInst, { name, instance } ) + end + + self:internalRunSuiteByInstances( listOfNameAndInst ) + end + + function M.LuaUnit.run(...) + -- Run some specific test classes. + -- If no arguments are passed, run the class names specified on the + -- command line. If no class name is specified on the command line + -- run all classes whose name starts with 'Test' + -- + -- If arguments are passed, they must be strings of the class names + -- that you want to run or generic command line arguments (-o, -p, -v, ...) + local runner = M.LuaUnit.new() + return runner:runSuite(...) + end + + function M.LuaUnit:registerSuite() + -- register the current instance into our global array of instances + -- print('-> Register suite') + M.LuaUnit.instances[ #M.LuaUnit.instances+1 ] = self + end + + function M.unregisterCurrentSuite() + -- force unregister the last registered suite + table.remove(M.LuaUnit.instances, #M.LuaUnit.instances) + end + + function M.LuaUnit:unregisterSuite() + -- print('<- Unregister suite') + -- remove our current instqances from the global array of instances + local instanceIdx = nil + for i, instance in ipairs(M.LuaUnit.instances) do + if instance == self then + instanceIdx = i + break + end + end + + if instanceIdx ~= nil then + table.remove(M.LuaUnit.instances, instanceIdx) + -- print('Unregister done') + end + + end + + function M.LuaUnit:initFromArguments( ... ) + --[[Parses all arguments from either command-line or direct call and set internal + flags of LuaUnit runner according to it. + + Return the list of names which were possibly passed on the command-line or as arguments + ]] + local args = {...} + if type(args[1]) == 'table' and args[1].__class__ == 'LuaUnit' then + -- run was called with the syntax M.LuaUnit:runSuite() + -- we support both M.LuaUnit.run() and M.LuaUnit:run() + -- strip out the first argument self to make it a command-line argument list + table.remove(args,1) + end + + if #args == 0 then + args = cmdline_argv + end + + local options = pcall_or_abort( M.LuaUnit.parseCmdLine, args ) + + -- We expect these option fields to be either `nil` or contain + -- valid values, so it's safe to always copy them directly. + self.verbosity = options.verbosity + self.quitOnError = options.quitOnError + self.quitOnFailure = options.quitOnFailure + + self.exeRepeat = options.exeRepeat + self.patternIncludeFilter = options.pattern + self.shuffle = options.shuffle + + options.output = options.output or os.getenv('LUAUNIT_OUTPUT') + options.fname = options.fname or os.getenv('LUAUNIT_JUNIT_FNAME') + + if options.output then + if options.output:lower() == 'junit' and options.fname == nil then + print('With junit output, a filename must be supplied with -n or --name') + os.exit(-1) + end + pcall_or_abort(self.setOutputType, self, options.output, options.fname) + end + + return options.testNames + end + + function M.LuaUnit:runSuite( ... ) + testNames = self:initFromArguments(...) + self:registerSuite() + self:internalRunSuiteByNames( testNames or M.LuaUnit.collectTests() ) + self:unregisterSuite() + return self.result.notSuccessCount + end + + function M.LuaUnit:runSuiteByInstances( listOfNameAndInst, commandLineArguments ) + --[[ + Run all test functions or tables provided as input. + + Input: a list of { name, instance } + instance can either be a function or a table containing test functions starting with the prefix "test" + + return the number of failures and errors, 0 meaning success + ]] + -- parse the command-line arguments + testNames = self:initFromArguments( commandLineArguments ) + self:registerSuite() + self:internalRunSuiteByInstances( listOfNameAndInst ) + self:unregisterSuite() + return self.result.notSuccessCount + end + + + +-- class LuaUnit + +-- For compatbility with LuaUnit v2 +M.run = M.LuaUnit.run +M.Run = M.LuaUnit.run + +function M:setVerbosity( verbosity ) + -- set the verbosity value (as integer) + M.LuaUnit.verbosity = verbosity +end +M.set_verbosity = M.setVerbosity +M.SetVerbosity = M.setVerbosity + + +return M + diff --git a/framework/lualib/thirdparty/lume/lume.lua b/framework/lualib/thirdparty/lume/lume.lua new file mode 100644 index 0000000..fcd30d0 --- /dev/null +++ b/framework/lualib/thirdparty/lume/lume.lua @@ -0,0 +1,818 @@ +-- +-- https://github.com/rxi/lume +-- +-- Copyright (c) 2020 rxi +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy of +-- this software and associated documentation files (the "Software"), to deal in +-- the Software without restriction, including without limitation the rights to +-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +-- of the Software, and to permit persons to whom the Software is furnished to do +-- so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in all +-- copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- +local lume = { + _version = "2.3.0", +} + +local pairs, ipairs = pairs, ipairs +local type, assert, unpack = type, assert, unpack or table.unpack +local tostring, tonumber = tostring, tonumber +local math_floor = math.floor +local math_ceil = math.ceil +local math_atan2 = math.atan2 or math.atan +local math_sqrt = math.sqrt +local math_abs = math.abs + +local noop = function() +end + +local identity = function(x) + return x +end + +local patternescape = function(str) + return str:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%1") +end + +local absindex = function(len, i) + return i < 0 and (len + i + 1) or i +end + +local iscallable = function(x) + if type(x) == "function" then + return true + end + local mt = getmetatable(x) + return mt and mt.__call ~= nil +end + +local getiter = function(x) + if lume.isarray(x) then + return ipairs + elseif type(x) == "table" then + return pairs + end + error("expected table", 3) +end + +local iteratee = function(x) + if x == nil then + return identity + end + if iscallable(x) then + return x + end + if type(x) == "table" then + return function(z) + for k, v in pairs(x) do + if z[k] ~= v then + return false + end + end + return true + end + end + return function(z) + return z[x] + end +end + +function lume.clamp(x, min, max) + return x < min and min or (x > max and max or x) +end + +function lume.round(x, increment) + if increment then + return lume.round(x / increment) * increment + end + return x >= 0 and math_floor(x + .5) or math_ceil(x - .5) +end + +function lume.sign(x) + return x < 0 and -1 or 1 +end + +function lume.lerp(a, b, amount) + return a + (b - a) * lume.clamp(amount, 0, 1) +end + +function lume.smooth(a, b, amount) + local t = lume.clamp(amount, 0, 1) + local m = t * t * (3 - 2 * t) + return a + (b - a) * m +end + +function lume.pingpong(x) + return 1 - math_abs(1 - x % 2) +end + +function lume.distance(x1, y1, x2, y2, squared) + local dx = x1 - x2 + local dy = y1 - y2 + local s = dx * dx + dy * dy + return squared and s or math_sqrt(s) +end + +function lume.angle(x1, y1, x2, y2) + return math_atan2(y2 - y1, x2 - x1) +end + +function lume.vector(angle, magnitude) + return math.cos(angle) * magnitude, math.sin(angle) * magnitude +end + +function lume.random(a, b) + if not a then + a, b = 0, 1 + end + if not b then + b = 0 + end + return a + math.random() * (b - a) +end + +function lume.randomchoice(t) + return t[math.random(#t)] +end + +function lume.weightedchoice(t) + local sum = 0 + for _, v in pairs(t) do + assert(v >= 0, "weight value less than zero") + sum = sum + v + end + assert(sum ~= 0, "all weights are zero") + local rnd = lume.random(sum) + for k, v in pairs(t) do + if rnd < v then + return k + end + rnd = rnd - v + end +end + +function lume.isarray(x) + return type(x) == "table" and x[1] ~= nil +end + +function lume.push(t, ...) + local n = select("#", ...) + for i = 1, n do + t[#t + 1] = select(i, ...) + end + return ... +end + +function lume.remove(t, x) + local iter = getiter(t) + for i, v in iter(t) do + if v == x then + if lume.isarray(t) then + table.remove(t, i) + break + else + t[i] = nil + break + end + end + end + return x +end + +function lume.clear(t) + local iter = getiter(t) + for k in iter(t) do + t[k] = nil + end + return t +end + +function lume.extend(t, ...) + for i = 1, select("#", ...) do + local x = select(i, ...) + if x then + for k, v in pairs(x) do + t[k] = v + end + end + end + return t +end + +function lume.shuffle(t) + local rtn = {} + for i = 1, #t do + local r = math.random(i) + if r ~= i then + rtn[i] = rtn[r] + end + rtn[r] = t[i] + end + return rtn +end + +function lume.sort(t, comp) + local rtn = lume.clone(t) + if comp then + if type(comp) == "string" then + table.sort(rtn, function(a, b) + return a[comp] < b[comp] + end) + else + table.sort(rtn, comp) + end + else + table.sort(rtn) + end + return rtn +end + +function lume.array(...) + local t = {} + for x in ... do + t[#t + 1] = x + end + return t +end + +function lume.each(t, fn, ...) + local iter = getiter(t) + if type(fn) == "string" then + for _, v in iter(t) do + v[fn](v, ...) + end + else + for _, v in iter(t) do + fn(v, ...) + end + end + return t +end + +function lume.map(t, fn) + fn = iteratee(fn) + local iter = getiter(t) + local rtn = {} + for k, v in iter(t) do + rtn[k] = fn(v) + end + return rtn +end + +function lume.all(t, fn) + fn = iteratee(fn) + local iter = getiter(t) + for _, v in iter(t) do + if not fn(v) then + return false + end + end + return true +end + +function lume.any(t, fn) + fn = iteratee(fn) + local iter = getiter(t) + for _, v in iter(t) do + if fn(v) then + return true + end + end + return false +end + +function lume.reduce(t, fn, first) + local started = first ~= nil + local acc = first + local iter = getiter(t) + for _, v in iter(t) do + if started then + acc = fn(acc, v) + else + acc = v + started = true + end + end + assert(started, "reduce of an empty table with no first value") + return acc +end + +function lume.unique(t) + local rtn = {} + for k in pairs(lume.invert(t)) do + rtn[#rtn + 1] = k + end + return rtn +end + +function lume.filter(t, fn, retainkeys) + fn = iteratee(fn) + local iter = getiter(t) + local rtn = {} + if retainkeys then + for k, v in iter(t) do + if fn(v) then + rtn[k] = v + end + end + else + for _, v in iter(t) do + if fn(v) then + rtn[#rtn + 1] = v + end + end + end + return rtn +end + +function lume.reject(t, fn, retainkeys) + fn = iteratee(fn) + local iter = getiter(t) + local rtn = {} + if retainkeys then + for k, v in iter(t) do + if not fn(v) then + rtn[k] = v + end + end + else + for _, v in iter(t) do + if not fn(v) then + rtn[#rtn + 1] = v + end + end + end + return rtn +end + +function lume.merge(...) + local rtn = {} + for i = 1, select("#", ...) do + local t = select(i, ...) + local iter = getiter(t) + for k, v in iter(t) do + rtn[k] = v + end + end + return rtn +end + +function lume.concat(...) + local rtn = {} + for i = 1, select("#", ...) do + local t = select(i, ...) + if t ~= nil then + local iter = getiter(t) + for _, v in iter(t) do + rtn[#rtn + 1] = v + end + end + end + return rtn +end + +function lume.find(t, value) + local iter = getiter(t) + for k, v in iter(t) do + if v == value then + return k + end + end + return nil +end + +function lume.match(t, fn) + fn = iteratee(fn) + local iter = getiter(t) + for k, v in iter(t) do + if fn(v) then + return v, k + end + end + return nil +end + +function lume.count(t, fn) + local count = 0 + local iter = getiter(t) + if fn then + fn = iteratee(fn) + for _, v in iter(t) do + if fn(v) then + count = count + 1 + end + end + else + if lume.isarray(t) then + return #t + end + for _ in iter(t) do + count = count + 1 + end + end + return count +end + +function lume.slice(t, i, j) + i = i and absindex(#t, i) or 1 + j = j and absindex(#t, j) or #t + local rtn = {} + for x = i < 1 and 1 or i, j > #t and #t or j do + rtn[#rtn + 1] = t[x] + end + return rtn +end + +function lume.first(t, n) + if not n then + return t[1] + end + return lume.slice(t, 1, n) +end + +function lume.last(t, n) + if not n then + return t[#t] + end + return lume.slice(t, -n, -1) +end + +function lume.invert(t) + local rtn = {} + for k, v in pairs(t) do + rtn[v] = k + end + return rtn +end + +function lume.pick(t, ...) + local rtn = {} + for i = 1, select("#", ...) do + local k = select(i, ...) + rtn[k] = t[k] + end + return rtn +end + +function lume.keys(t) + local rtn = {} + local iter = getiter(t) + for k in iter(t) do + rtn[#rtn + 1] = k + end + return rtn +end + +function lume.clone(t) + local rtn = {} + for k, v in pairs(t) do + rtn[k] = v + end + return rtn +end + +function lume.fn(fn, ...) + assert(iscallable(fn), "expected a function as the first argument") + local args = {...} + return function(...) + local a = lume.concat(args, {...}) + return fn(unpack(a)) + end +end + +function lume.once(fn, ...) + local f = lume.fn(fn, ...) + local done = false + return function(...) + if done then + return + end + done = true + return f(...) + end +end + +local memoize_fnkey = {} +local memoize_nil = {} + +function lume.memoize(fn) + local cache = {} + return function(...) + local c = cache + for i = 1, select("#", ...) do + local a = select(i, ...) or memoize_nil + c[a] = c[a] or {} + c = c[a] + end + c[memoize_fnkey] = c[memoize_fnkey] or {fn(...)} + return unpack(c[memoize_fnkey]) + end +end + +function lume.combine(...) + local n = select('#', ...) + if n == 0 then + return noop + end + if n == 1 then + local fn = select(1, ...) + if not fn then + return noop + end + assert(iscallable(fn), "expected a function or nil") + return fn + end + local funcs = {} + for i = 1, n do + local fn = select(i, ...) + if fn ~= nil then + assert(iscallable(fn), "expected a function or nil") + funcs[#funcs + 1] = fn + end + end + return function(...) + for _, f in ipairs(funcs) do + f(...) + end + end +end + +function lume.call(fn, ...) + if fn then + return fn(...) + end +end + +function lume.time(fn, ...) + local start = os.clock() + local rtn = {fn(...)} + return (os.clock() - start), unpack(rtn) +end + +local lambda_cache = {} + +function lume.lambda(str) + if not lambda_cache[str] then + local args, body = str:match([[^([%w,_ ]-)%->(.-)$]]) + assert(args and body, "bad string lambda") + local s = "return function(" .. args .. ")\nreturn " .. body .. "\nend" + lambda_cache[str] = lume.dostring(s) + end + return lambda_cache[str] +end + +local serialize + +local serialize_map = { + ["boolean"] = tostring, + ["nil"] = tostring, + ["string"] = function(v) + return string.format("%q", v) + end, + ["number"] = function(v) + if v ~= v then + return "0/0" -- nan + elseif v == 1 / 0 then + return "1/0" -- inf + elseif v == -1 / 0 then + return "-1/0" + end -- -inf + return tostring(v) + end, + ["table"] = function(t, stk) + stk = stk or {} + if stk[t] then + error("circular reference") + end + local rtn = {} + stk[t] = true + for k, v in pairs(t) do + rtn[#rtn + 1] = "[" .. serialize(k, stk) .. "]=" .. serialize(v, stk) + end + stk[t] = nil + return "{" .. table.concat(rtn, ",") .. "}" + end, +} + +setmetatable(serialize_map, { + __index = function(_, k) + error("unsupported serialize type: " .. k) + end, +}) + +serialize = function(x, stk) + return serialize_map[type(x)](x, stk) +end + +function lume.serialize(x) + return serialize(x) +end + +function lume.deserialize(str) + return lume.dostring("return " .. str) +end + +function lume.split(str, sep) + if not sep then + return lume.array(str:gmatch("([%S]+)")) + else + assert(sep ~= "", "empty separator") + local psep = patternescape(sep) + return lume.array((str .. sep):gmatch("(.-)(" .. psep .. ")")) + end +end + +function lume.trim(str, chars) + if not chars then + return str:match("^[%s]*(.-)[%s]*$") + end + chars = patternescape(chars) + return str:match("^[" .. chars .. "]*(.-)[" .. chars .. "]*$") +end + +function lume.wordwrap(str, limit) + limit = limit or 72 + local check + if type(limit) == "number" then + check = function(s) + return #s >= limit + end + else + check = limit + end + local rtn = {} + local line = "" + for word, spaces in str:gmatch("(%S+)(%s*)") do + local s = line .. word + if check(s) then + table.insert(rtn, line .. "\n") + line = word + else + line = s + end + for c in spaces:gmatch(".") do + if c == "\n" then + table.insert(rtn, line .. "\n") + line = "" + else + line = line .. c + end + end + end + table.insert(rtn, line) + return table.concat(rtn) +end + +function lume.format(str, vars) + if not vars then + return str + end + local f = function(x) + return tostring(vars[x] or vars[tonumber(x)] or "{" .. x .. "}") + end + return (str:gsub("{(.-)}", f)) +end + +function lume.trace(...) + local info = debug.getinfo(2, "Sl") + local t = {info.short_src .. ":" .. info.currentline .. ":"} + for i = 1, select("#", ...) do + local x = select(i, ...) + if type(x) == "number" then + x = string.format("%g", lume.round(x, .01)) + end + t[#t + 1] = tostring(x) + end + print(table.concat(t, " ")) +end + +function lume.dostring(str) + return assert((loadstring or load)(str))() +end + +function lume.uuid() + local fn = function(x) + local r = math.random(16) - 1 + r = (x == "x") and (r + 1) or (r % 4) + 9 + return ("0123456789abcdef"):sub(r, r) + end + return (("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"):gsub("[xy]", fn)) +end + +function lume.hotswap(modname) + local oldglobal = lume.clone(_G) + local updated = {} + local function update(old, new) + if updated[old] then + return + end + updated[old] = true + local oldmt, newmt = getmetatable(old), getmetatable(new) + if oldmt and newmt then + update(oldmt, newmt) + end + for k, v in pairs(new) do + if type(v) == "table" then + update(old[k], v) + else + old[k] = v + end + end + end + local err = nil + local function onerror(e) + for k in pairs(_G) do + _G[k] = oldglobal[k] + end + err = lume.trim(e) + end + local ok, oldmod = pcall(require, modname) + oldmod = ok and oldmod or nil + xpcall(function() + package.loaded[modname] = nil + local newmod = require(modname) + if type(oldmod) == "table" then + update(oldmod, newmod) + end + for k, v in pairs(oldglobal) do + if v ~= _G[k] and type(v) == "table" then + update(v, _G[k]) + _G[k] = v + end + end + end, onerror) + package.loaded[modname] = oldmod + if err then + return nil, err + end + return oldmod +end + +local ripairs_iter = function(t, i) + i = i - 1 + local v = t[i] + if v ~= nil then + return i, v + end +end + +function lume.ripairs(t) + return ripairs_iter, t, (#t + 1) +end + +function lume.color(str, mul) + mul = mul or 1 + local r, g, b, a + r, g, b = str:match("#(%x%x)(%x%x)(%x%x)") + if r then + r = tonumber(r, 16) / 0xff + g = tonumber(g, 16) / 0xff + b = tonumber(b, 16) / 0xff + a = 1 + elseif str:match("rgba?%s*%([%d%s%.,]+%)") then + local f = str:gmatch("[%d.]+") + r = (f() or 0) / 0xff + g = (f() or 0) / 0xff + b = (f() or 0) / 0xff + a = f() or 1 + else + error(("bad color string '%s'"):format(str)) + end + return r * mul, g * mul, b * mul, a * mul +end + +local chain_mt = {} +chain_mt.__index = lume.map(lume.filter(lume, iscallable, true), function(fn) + return function(self, ...) + self._value = fn(self._value, ...) + return self + end +end) +chain_mt.__index.result = function(x) + return x._value +end + +function lume.chain(value) + return setmetatable({ + _value = value, + }, chain_mt) +end + +setmetatable(lume, { + __call = function(_, ...) + return lume.chain(...) + end, +}) + +return lume diff --git a/framework/lualib/thirdparty/mimetypes.lua b/framework/lualib/thirdparty/mimetypes.lua new file mode 100644 index 0000000..b6e7624 --- /dev/null +++ b/framework/lualib/thirdparty/mimetypes.lua @@ -0,0 +1,316 @@ +-- mimetypes.lua +-- Version 1.0.0 + +--[[ +Copyright (c) 2011 Matthew "LeafStorm" Frazier + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +====== + +In addition, the MIME types contained in the Software were +originally obtained from the Python 2.7.1 ``mimetypes.py`` module, +though they have been considerably modified and augmented. +Said file was made available under the Python Software Foundation +license (http://python.org/psf/license/). +]] +-- This table is the one that actually contains the exported functions. + +local mimetypes = {} + +mimetypes.version = "1.0.0" + +-- Extracts the extension from a filename and returns it. +-- The extension must be at the end of the string, and preceded by a dot and +-- at least one other character. Only the last part will be returned (so +-- "package-1.2.tar.gz" will return "gz"). +-- If there is no extension, this function will return nil. + +local function extension(filename) + return filename:match(".+%.([%a%d]+)$") +end + +-- Creates a deep copy of the given table. + +local function copy(tbl) + local ntbl = {} + for key, value in pairs(tbl) do + if type(value) == "table" then + ntbl[key] = copy(value) + else + ntbl[key] = value + end + end + return ntbl +end + +-- This is the default MIME type database. +-- It is a table with two members - "extensions" and "filenames". +-- The filenames table maps complete file names (like README) to MIME types. +-- The extensions just maps the files' extensions (like jpg) to types. + +local defaultdb = {extensions = {}, filenames = {}} + +local extensions = defaultdb.extensions +local filenames = defaultdb.filenames + +-- The MIME types are sorted first by major type ("application/"), then by +-- extension. Remember to not include the dot on the extension. + +-- application/ +extensions["a"] = "application/octet-stream" +extensions["ai"] = "application/postscript" +extensions["asc"] = "application/pgp-signature" +extensions["atom"] = "application/atom+xml" +extensions["bcpio"] = "application/x-bcpio" +extensions["bin"] = "application/octet-stream" +extensions["bz2"] = "application/x-bzip2" +extensions["cab"] = "application/vnd.ms-cab-compressed" +extensions["chm"] = "application/vnd.ms-htmlhelp" +extensions["class"] = "application/octet-stream" +extensions["cdf"] = "application/x-netcdf" +extensions["cpio"] = "application/x-cpio" +extensions["csh"] = "application/x-csh" +extensions["deb"] = "application/x-deb" +extensions["dll"] = "application/octet-stream" +extensions["dmg"] = "application/x-apple-diskimage" +extensions["doc"] = "application/msword" +extensions["docx"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document" +extensions["dot"] = "application/msword" +extensions["dvi"] = "application/x-dvi" +extensions["eps"] = "application/postscript" +extensions["exe"] = "application/octet-stream" +extensions["gtar"] = "application/x-gtar" +extensions["gz"] = "application/x-gzip" +extensions["hdf"] = "application/x-hdf" +extensions["hqx"] = "application/mac-binhex40" +extensions["iso"] = "application/octet-stream" +extensions["jar"] = "application/java-archive" +extensions["js"] = "application/javascript" +extensions["json"] = "application/json" +extensions["latex"] = "application/x-latex" +extensions["man"] = "application/x-troff-man" +extensions["me"] = "application/x-troff-me" +extensions["mif"] = "application/x-mif" +extensions["ms"] = "application/x-troff-ms" +extensions["nc"] = "application/x-netcdf" +extensions["o"] = "application/octet-stream" +extensions["obj"] = "application/octet-stream" +extensions["oda"] = "application/oda" +extensions["odt"] = "application/vnd.oasis.opendocument.text" +extensions["odp"] = "application/vnd.oasis.opendocument.presentation" +extensions["ods"] = "application/vnd.oasis.opendocument.spreadsheet" +extensions["odg"] = "application/vnd.oasis.opendocument.graphics" +extensions["p12"] = "application/x-pkcs12" +extensions["p7c"] = "application/pkcs7-mime" +extensions["pdf"] = "application/pdf" +extensions["pfx"] = "application/x-pkcs12" +extensions["pgp"] = "application/pgp-encrypted" +extensions["pot"] = "application/vnd.ms-powerpoint" +extensions["ppa"] = "application/vnd.ms-powerpoint" +extensions["pps"] = "application/vnd.ms-powerpoint" +extensions["ppt"] = "application/vnd.ms-powerpoint" +extensions["pptx"] = "application/vnd.openxmlformats-officedocument.presentationml.presentation" +extensions["ps"] = "application/postscript" +extensions["pwz"] = "application/vnd.ms-powerpoint" +extensions["pyc"] = "application/x-python-code" +extensions["pyo"] = "application/x-python-code" +extensions["ram"] = "application/x-pn-realaudio" +extensions["rar"] = "application/x-rar-compressed" +extensions["rdf"] = "application/rdf+xml" +extensions["rpm"] = "application/x-redhat-package-manager" +extensions["rss"] = "application/rss+xml" +extensions["rtf"] = "application/rtf" +extensions["roff"] = "application/x-troff" +extensions["sh"] = "application/x-sh" +extensions["shar"] = "application/x-shar" +extensions["sig"] = "application/pgp-signature" +extensions["sit"] = "application/x-stuffit" +extensions["smil"] = "application/smil+xml" +extensions["so"] = "application/octet-stream" +extensions["src"] = "application/x-wais-source" +extensions["sv4cpio"] = "application/x-sv4cpio" +extensions["sv4crc"] = "application/x-sv4crc" +extensions["swf"] = "application/x-shockwave-flash" +extensions["t"] = "application/x-troff" +extensions["tar"] = "application/x-tar" +extensions["tcl"] = "application/x-tcl" +extensions["tex"] = "application/x-tex" +extensions["texi"] = "application/x-texinfo" +extensions["texinfo"] = "application/x-texinfo" +extensions["torrent"] = "application/x-bittorrent" +extensions["tr"] = "application/x-troff" +extensions["ustar"] = "application/x-ustar" +extensions["wiz"] = "application/msword" +extensions["wsdl"] = "application/wsdl+xml" +extensions["xht"] = "application/xhtml+xml" +extensions["xhtml"] = "application/xhtml+xml" +extensions["xlb"] = "application/vnd.ms-excel" +extensions["xls"] = "application/vnd.ms-excel" +extensions["xlsx"] = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" +extensions["xpdl"] = "application/xml" +extensions["xsl"] = "application/xml" +extensions["xul"] = "application/vnd.mozilla.xul+xml" +extensions["zip"] = "application/zip" + +-- audio/ +extensions["aif"] = "audio/x-aiff" +extensions["aifc"] = "audio/x-aiff" +extensions["aiff"] = "audio/x-aiff" +extensions["au"] = "audio/basic" +extensions["flac"] = "audio/x-flac" +extensions["mid"] = "audio/midi" +extensions["midi"] = "audio/midi" +extensions["mp2"] = "audio/mpeg" +extensions["mp3"] = "audio/mpeg" +extensions["m3u"] = "audio/x-mpegurl" +extensions["oga"] = "audio/ogg" +extensions["ogg"] = "audio/ogg" +extensions["ra"] = "audio/x-pn-realaudio" +extensions["snd"] = "audio/basic" +extensions["wav"] = "audio/x-wav" + +-- image/ +extensions["bmp"] = "image/x-ms-bmp" +extensions["djv"] = "image/vnd.djvu" +extensions["djvu"] = "image/vnd.djvu" +extensions["gif"] = "image/gif" +extensions["ico"] = "image/vnd.microsoft.icon" +extensions["ief"] = "image/ief" +extensions["jpe"] = "image/jpeg" +extensions["jpeg"] = "image/jpeg" +extensions["jpg"] = "image/jpeg" +extensions["pbm"] = "image/x-portable-bitmap" +extensions["pgm"] = "image/x-portable-graymap" +extensions["png"] = "image/png" +extensions["pnm"] = "image/x-portable-anymap" +extensions["ppm"] = "image/x-portable-pixmap" +extensions["psd"] = "image/vnd.adobe.photoshop" +extensions["ras"] = "image/x-cmu-raster" +extensions["rgb"] = "image/x-rgb" +extensions["svg"] = "image/svg+xml" +extensions["svgz"] = "image/svg+xml" +extensions["tif"] = "image/tiff" +extensions["tiff"] = "image/tiff" +extensions["xbm"] = "image/x-xbitmap" +extensions["xpm"] = "image/x-xpixmap" +extensions["xwd"] = "image/x-xwindowdump" + +-- message/ +extensions["eml"] = "message/rfc822" +extensions["mht"] = "message/rfc822" +extensions["mhtml"] = "message/rfc822" +extensions["nws"] = "message/rfc822" + +-- model/ +extensions["vrml"] = "model/vrml" + +-- text/ +extensions["asm"] = "text/x-asm" +extensions["bat"] = "text/plain" +extensions["c"] = "text/x-c" +extensions["cc"] = "text/x-c" +extensions["conf"] = "text/plain" +extensions["cpp"] = "text/x-c" +extensions["css"] = "text/css" +extensions["csv"] = "text/csv" +extensions["diff"] = "text/x-diff" +extensions["etx"] = "text/x-setext" +extensions["gemspec"] = "text/x-ruby" +extensions["h"] = "text/x-c" +extensions["hh"] = "text/x-c" +extensions["htm"] = "text/html" +extensions["html"] = "text/html" +extensions["ics"] = "text/calendar" +extensions["java"] = "text/x-java" +extensions["ksh"] = "text/plain" +extensions["lua"] = "text/x-lua" +extensions["manifest"] = "text/cache-manifest" +extensions["md"] = "text/x-markdown" +extensions["p"] = "text/x-pascal" +extensions["pas"] = "text/x-pascal" +extensions["pl"] = "text/x-perl" +extensions["pm"] = "text/x-perl" +extensions["py"] = "text/x-python" +extensions["rb"] = "text/x-ruby" +extensions["ru"] = "text/x-ruby" +extensions["rockspec"] = "text/x-lua" +extensions["rtx"] = "text/richtext" +extensions["s"] = "text/x-asm" +extensions["sgm"] = "text/x-sgml" +extensions["sgml"] = "text/x-sgml" +extensions["text"] = "text/plain" +extensions["tsv"] = "text/tab-separated-values" +extensions["txt"] = "text/plain" +extensions["vcf"] = "text/x-vcard" +extensions["vcs"] = "text/x-vcalendar" +extensions["xml"] = "text/xml" +extensions["yaml"] = "text/yaml" +extensions["yml"] = "text/yml" + +-- video/ +extensions["avi"] = "video/x-msvideo" +extensions["flv"] = "video/x-flv" +extensions["m1v"] = "video/mpeg" +extensions["mov"] = "video/quicktime" +extensions["movie"] = "video/x-sgi-movie" +extensions["mng"] = "video/x-mng" +extensions["mp4"] = "video/mp4" +extensions["mpa"] = "video/mpeg" +extensions["mpe"] = "video/mpeg" +extensions["mpeg"] = "video/mpeg" +extensions["mpg"] = "video/mpeg" +extensions["ogv"] = "video/ogg" +extensions["qt"] = "video/quicktime" + +-- This contains filename overrides for certain files, like README files. +-- Sort them in the same order as extensions. + +filenames["COPYING"] = "text/plain" +filenames["LICENSE"] = "text/plain" +filenames["Makefile"] = "text/x-makefile" +filenames["README"] = "text/plain" + +-- Creates a copy of the MIME types database for customization. + +function mimetypes.copy(db) + db = db or defaultdb + return copy(db) +end + +-- Guesses the MIME type of the file with the given name. +-- It is returned as a string. If the type cannot be guessed, then nil is +-- returned. + +function mimetypes.guess(filename, db) + db = db or defaultdb + if db.filenames[filename] then + return db.filenames[filename] + end + local ext = extension(filename) + if ext then + return db.extensions[ext] + end + return nil +end + +return mimetypes diff --git a/framework/lualib/thirdparty/moses/moses.lua b/framework/lualib/thirdparty/moses/moses.lua new file mode 100644 index 0000000..64bf9e9 --- /dev/null +++ b/framework/lualib/thirdparty/moses/moses.lua @@ -0,0 +1,3477 @@ +--- Utility-belt library for functional programming in Lua ([source](http://github.com/Yonaba/Moses)) +-- @author [Roland Yonaba](http://github.com/Yonaba) +-- @copyright 2012-2018 +-- @license [MIT](http://www.opensource.org/licenses/mit-license.php) +-- @release 2.1.0 +-- @module moses +-- @set sort=true + +local _MODULEVERSION = "2.1.0" + +-- Internalisation +local next, type, pcall = next, type, pcall +local setmetatable, getmetatable = setmetatable, getmetatable +local t_insert, t_sort = table.insert, table.sort +local t_remove, t_concat = table.remove, table.concat +local randomseed, random, huge = math.randomseed, math.random, math.huge +local floor, max, min, ceil = math.floor, math.max, math.min, math.ceil +local wrap = coroutine.wrap +local yield = coroutine.yield +local rawget = rawget +local unpack = table.unpack or unpack +local pairs, ipairs = pairs, ipairs +local error = error +local clock = os and os.clock or nil +local M = {} + +-- ======== Private helpers + +local function f_max(a, b) + return a > b +end +local function f_min(a, b) + return a < b +end + +local function count(t) -- raw count of items in an map-table + local i = 0 + for k, v in pairs(t) do + i = i + 1 + end + return i +end + +local function extract(list, comp, transform, ...) -- extracts value from a list + transform = transform or M.identity + local _ans + for k, v in pairs(list) do + if not _ans then + _ans = transform(v, ...) + else + local val = transform(v, ...) + _ans = comp(_ans, val) and _ans or val + end + end + return _ans +end + +local function partgen(t, n, f, pad) -- generates array partitions + for i = 0, #t, n do + local s = M.slice(t, i + 1, i + n) + if #s > 0 then + while (#s < n and pad) do + s[#s + 1] = pad + end + f(s) + end + end +end + +local function partgen2(t, n, f, pad) -- generates overlapping array partitions + for i = 0, #t, n - 1 do + local s = M.slice(t, i + 1, i + n) + if #s > 0 and i + 1 < #t then + while (#s < n and pad) do + s[#s + 1] = pad + end + f(s) + end + end +end + +local function partgen3(t, n, f, pad) -- generates sliding array partitions + for i = 0, #t, 1 do + local s = M.slice(t, i + 1, i + n) + if #s > 0 and i + n <= #t then + while (#s < n and pad) do + s[#s + 1] = pad + end + f(s) + end + end +end + +local function permgen(t, n, f) -- taken from PiL: http://www.lua.org/pil/9.3.html + if n == 0 then + f(t) + end + for i = 1, n do + t[n], t[i] = t[i], t[n] + permgen(t, n - 1, f) + t[n], t[i] = t[i], t[n] + end +end + +local function signum(a) + return a >= 0 and 1 or -1 +end + +-- Internal counter for unique ids generation +local unique_id_counter = -1 + +--- Operator functions +-- @section Operator functions + +M.operator = {} +--- Returns a + b. Aliased as `op.add`. +-- @name operator.add +-- @param a a value +-- @param b a value +-- @return a + b +M.operator.add = function(a, b) + return a + b +end + +--- Returns a - b. Aliased as `op.sub`. +-- @name operator.sub +-- @param a a value +-- @param b a value +-- @return a - b +M.operator.sub = function(a, b) + return a - b +end + +--- Returns a * b. Aliased as `op.mul`. +-- @name operator.mul +-- @param a a value +-- @param b a value +-- @return a * b +M.operator.mul = function(a, b) + return a * b +end + +--- Returns a / b. Aliased as `op.div`. +-- @name operator.div +-- @param a a value +-- @param b a value +-- @return a / b +M.operator.div = function(a, b) + return a / b +end + +--- Returns a % b. Aliased as `op.mod`. +-- @name operator.mod +-- @param a a value +-- @param b a value +-- @return a % b +M.operator.mod = function(a, b) + return a % b +end + +--- Returns a ^ b. Aliased as `op.exp`, `op.pow`. +-- @name operator.exp +-- @param a a value +-- @param b a value +-- @return a ^ b +M.operator.exp = function(a, b) + return a ^ b +end +M.operator.pow = M.operator.exp + +--- Returns -a. Aliased as `op.unm`, `op.neg`. +-- @name operator.unm +-- @param a a value +-- @return -a +M.operator.unm = function(a) + return -a +end +M.operator.neg = M.operator.unm + +--- Performs floor division (//) between `a` and `b`. It rounds the quotient towards minus infinity. +-- Aliased as `op.floordiv`. +-- @name operator.floordiv +-- @param a a value +-- @param b a value +-- @return a // b +M.operator.floordiv = function(a, b) + return floor(a / b) +end + +--- Performs integer division between `a` and `b`. Aliased as `op.intdiv`. +-- @name operator.intdiv +-- @param a a value +-- @param b a value +-- @return a / b +M.operator.intdiv = function(a, b) + return a >= 0 and floor(a / b) or ceil(a / b) +end + +--- Checks if a equals b. Aliased as `op.eq`. +-- @name operator.eq +-- @param a a value +-- @param b a value +-- @return a == b +M.operator.eq = function(a, b) + return a == b +end + +--- Checks if a not equals b. Aliased as `op.neq`. +-- @name operator.neq +-- @param a a value +-- @param b a value +-- @return a ~= b +M.operator.neq = function(a, b) + return a ~= b +end + +--- Checks if a is strictly less than b. Aliased as `op.lt`. +-- @name operator.lt +-- @param a a value +-- @param b a value +-- @return a < b +M.operator.lt = function(a, b) + return a < b +end + +--- Checks if a is strictly greater than b. Aliased as `op.gt`. +-- @name operator.gt +-- @param a a value +-- @param b a value +-- @return a > b +M.operator.gt = function(a, b) + return a > b +end + +--- Checks if a is less or equal to b. Aliased as `op.le`. +-- @name operator.le +-- @param a a value +-- @param b a value +-- @return a <= b +M.operator.le = function(a, b) + return a <= b +end + +--- Checks if a is greater or equal to b. Aliased as `op.ge`. +-- @name operator.ge +-- @param a a value +-- @param b a value +-- @return a >= b +M.operator.ge = function(a, b) + return a >= b +end + +--- Returns logical a and b. Aliased as `op.land`. +-- @name operator.land +-- @param a a value +-- @param b a value +-- @return a and b +M.operator.land = function(a, b) + return a and b +end + +--- Returns logical a or b. Aliased as `op.lor`. +-- @name operator.lor +-- @param a a value +-- @param b a value +-- @return a or b +M.operator.lor = function(a, b) + return a or b +end + +--- Returns logical not a. Aliased as `op.lnot`. +-- @name operator.lnot +-- @param a a value +-- @return not a +M.operator.lnot = function(a) + return not a +end + +--- Returns concatenation of a and b. Aliased as `op.concat`. +-- @name operator.concat +-- @param a a value +-- @param b a value +-- @return a .. b +M.operator.concat = function(a, b) + return a .. b +end + +--- Returns the length of a. Aliased as `op.len`. +-- @name operator.length +-- @param a a value +-- @return #a +M.operator.length = function(a) + return #a +end +M.operator.len = M.operator.length + +--- Table functions +-- @section Table functions + +--- Clears a table. All its values become nil. +-- @name clear +-- @param t a table +-- @return the given table, cleared. +function M.clear(t) + for k in pairs(t) do + t[k] = nil + end + return t +end + +--- Iterates on key-value pairs, calling `f (v, k)` at every step. +--
Aliased as `forEach`. +-- @name each +-- @param t a table +-- @param f a function, prototyped as `f (v, k)` +-- @see eachi +function M.each(t, f) + for index, value in pairs(t) do + f(value, index) + end +end + +--- Iterates on integer key-value pairs, calling `f(v, k)` every step. +-- Only applies to values located at integer keys. The table can be a sparse array. +-- Iteration will start from the lowest integer key found to the highest one. +--
Aliased as `forEachi`. +-- @name eachi +-- @param t a table +-- @param f a function, prototyped as `f (v, k)` +-- @see each +function M.eachi(t, f) + local lkeys = M.sort(M.select(M.keys(t), M.isInteger)) + for k, key in ipairs(lkeys) do + f(t[key], key) + end +end + +--- Collects values at given keys and return them wrapped in an array. +-- @name at +-- @param t a table +-- @param ... A variable number of keys to collect values +-- @return an array-list of values +function M.at(t, ...) + local values = {} + for i, key in ipairs({...}) do + values[#values + 1] = t[key] + end + return values +end + +--- Adjusts the value at a given key using a function or a value. In case `f` is a function, +-- it should be prototyped `f(v)`. It does not mutate the given table, but rather +-- returns a new array. In case the given `key` does not exist in `t`, it throws an error. +-- @param t a table +-- @param key a key +-- @param f a function, prototyped as `f(v)` or a value +function M.adjust(t, key, f) + if (t[key] == nil) then + error("key not existing in table") + end + local _t = M.clone(t) + _t[key] = type(f) == "function" and f(_t[key]) or f + return _t +end + +--- Counts occurrences of a given value in a table. Uses @{isEqual} to compare values. +-- @name count +-- @param t a table +-- @param[opt] val a value to be searched in the table. If not given, the @{size} of the table will be returned +-- @return the count of occurrences of the given value +-- @see countf +-- @see size +function M.count(t, val) + if val == nil then + return M.size(t) + end + local count = 0 + for k, v in pairs(t) do + if M.isEqual(v, val) then + count = count + 1 + end + end + return count +end + +--- Counts the number of values passing a predicate test. Same as @{count}, but uses an iterator. +-- Returns the count for values passing the test `f (v, k)` +-- @name countf +-- @param t a table +-- @param f an iterator function, prototyped as `f (v, k)` +-- @return the count of values validating the predicate +-- @see count +-- @see size +function M.countf(t, f) + local count = 0 + for k, v in pairs(t) do + if f(v, k) then + count = count + 1 + end + end + return count +end + +--- Checks if all values in a collection are equal. Uses an optional `comp` function which is used +-- to compare values and defaults to @{isEqual} when not given. +--
Aliased as `alleq`. +-- @name allEqual +-- @param t a table +-- @param[opt] comp a comparison function. Defaults to `isEqual` +-- @return `true` when all values in `t` are equal, `false` otherwise. +-- @see isEqual +function M.allEqual(t, comp) + local k, pivot = next(t) + for k, v in pairs(t) do + if comp then + if not comp(pivot, v) then + return false + end + else + if not M.isEqual(pivot, v) then + return false + end + end + end + return true +end + +--- Loops `n` times through a table. In case `n` is omitted, it will loop forever. +-- In case `n` is lower or equal to 0, it returns an empty function. +--
Aliased as `loop`. +-- @name cycle +-- @param t a table +-- @param[opt] n the number of loops +-- @return an iterator function yielding value-key pairs from the passed-in table. +function M.cycle(t, n) + n = n or 1 + if n <= 0 then + return M.noop + end + local k, fk + local i = 0 + while true do + return function() + k = k and next(t, k) or next(t) + fk = not fk and k or fk + if n then + i = (k == fk) and i + 1 or i + if i > n then + return + end + end + return t[k], k + end + end +end + +--- Maps `f (v, k)` on value-key pairs, collects and returns the results. +-- Uses `pairs` to iterate over elements in `t`. +--
Aliased as `collect`. +-- @name map +-- @param t a table +-- @param f an iterator function, prototyped as `f (v, k)` +-- @return a table of results +-- @see mapi +function M.map(t, f) + local _t = {} + for index, value in pairs(t) do + local k, kv, v = index, f(value, index) + _t[v and kv or k] = v or kv + end + return _t +end + +--- Maps `f (v, k)` on value-key pairs, collects and returns the results. +-- Uses `ipairs` to iterate over elements in `t`. +-- @name mapi +-- @param t a table +-- @param f an iterator function, prototyped as `f (v, k)` +-- @return a table of results +-- @see map +function M.mapi(t, f) + local _t = {} + for index, value in ipairs(t) do + local k, kv, v = index, f(value, index) + _t[v and kv or k] = v or kv + end + return _t +end + +--- Reduces a table, left-to-right. Folds the table from the first element to the last element +-- to a single value, using a given iterator and an initial state. +-- The iterator takes a state and a value and returns a new state. +--
Aliased as `inject`, `foldl`. +-- @name reduce +-- @param t a table +-- @param f an iterator function, prototyped as `f (state, value)` +-- @param[opt] state an initial state of reduction. Defaults to the first value in the table. +-- @return the final state of reduction +-- @see best +-- @see reduceRight +-- @see reduceBy +function M.reduce(t, f, state) + for k, value in pairs(t) do + if state == nil then + state = value + else + state = f(state, value) + end + end + return state +end + +--- Returns the best value passing a selector function. Acts as a special case of +-- @{reduce}, using the first value in `t` as an initial state. It thens folds the given table, +-- testing each of its values `v` and selecting the value passing the call `f(state,v)` every time. +-- @name best +-- @param t a table +-- @param f an iterator function, prototyped as `f (state, value)` +-- @return the final state of reduction +-- @see reduce +-- @see reduceRight +-- @see reduceBy +function M.best(t, f) + local _, state = next(t) + for k, value in pairs(t) do + if state == nil then + state = value + else + state = f(state, value) and state or value + end + end + return state +end + +--- Reduces values in a table passing a given predicate. Folds the table left-to-right, considering +-- only values validating a given predicate. +-- @name reduceBy +-- @param t a table +-- @param f an iterator function, prototyped as `f (state, value)` +-- @param pred a predicate function `pred (v, k)` to select values to be considered for reduction +-- @param[opt] state an initial state of reduction. Defaults to the first value in the table of selected values. +-- @param[optchain] ... optional args to be passed to `pred` +-- @return the final state of reduction +-- @see reduce +-- @see best +-- @see reduceRight +function M.reduceBy(t, f, pred, state) + return M.reduce(M.select(t, pred), f, state) +end + +--- Reduces a table, right-to-left. Folds the table from the last element to the first element +-- to single value, using a given iterator and an initial state. +-- The iterator takes a state and a value, and returns a new state. +--
Aliased as `injectr`, `foldr`. +-- @name reduceRight +-- @param t a table +-- @param f an iterator function, prototyped as `f (state, value)` +-- @param[opt] state an initial state of reduction. Defaults to the last value in the table. +-- @return the final state of reduction +-- @see reduce +-- @see best +-- @see reduceBy +function M.reduceRight(t, f, state) + return M.reduce(M.reverse(t), f, state) +end + +--- Reduces a table while saving intermediate states. Folds the table left-to-right +-- using a given iterator and an initial state. The iterator takes a state and a value, +-- and returns a new state. The result is an array of intermediate states. +--
Aliased as `mapr` +-- @name mapReduce +-- @param t a table +-- @param f an iterator function, prototyped as `f (state, value)` +-- @param[opt] state an initial state of reduction. Defaults to the first value in the table. +-- @return an array of states +-- @see mapReduceRight +function M.mapReduce(t, f, state) + local _t = {} + for i, value in pairs(t) do + _t[i] = not state and value or f(state, value) + state = _t[i] + end + return _t +end + +--- Reduces a table while saving intermediate states. Folds the table right-to-left +-- using a given iterator and an initial state. The iterator takes a state and a value, +-- and returns a new state. The result is an array of intermediate states. +--
Aliased as `maprr` +-- @name mapReduceRight +-- @param t a table +-- @param f an iterator function, prototyped as `f (state, value)` +-- @param[opt] state an initial state of reduction. Defaults to the last value in the table. +-- @return an array of states +-- @see mapReduce +function M.mapReduceRight(t, f, state) + return M.mapReduce(M.reverse(t), f, state) +end + +--- Performs a linear search for a value in a table. It does not work for nested tables. +-- The given value can be a function prototyped as `f (v, value)` which should return true when +-- any v in the table equals the value being searched. +--
Aliased as `any`, `some`, `contains` +-- @name include +-- @param t a table +-- @param value a value to search for +-- @return a boolean : `true` when found, `false` otherwise +-- @see detect +function M.include(t, value) + local _iter = (type(value) == "function") and value or M.isEqual + for k, v in pairs(t) do + if _iter(v, value) then + return true + end + end + return false +end + +--- Performs a linear search for a value in a table. Returns the key of the value if found. +-- The given value can be a function prototyped as `f (v, value)` which should return true when +-- any v in the table equals the value being searched. This function is similar to @{find}, +-- which is mostly meant to work with array. +-- @name detect +-- @param t a table +-- @param value a value to search for +-- @return the key of the value when found or __nil__ +-- @see include +-- @see find +function M.detect(t, value) + local _iter = (type(value) == "function") and value or M.isEqual + for key, arg in pairs(t) do + if _iter(arg, value) then + return key + end + end +end + +--- Returns all values having specified keys `props`. +-- @name where +-- @param t a table +-- @param props a set of keys +-- @return an array of values from the passed-in table +-- @see findWhere +function M.where(t, props) + local r = + M.select( + t, + function(v) + for key in pairs(props) do + if v[key] ~= props[key] then + return false + end + end + return true + end + ) + return #r > 0 and r or nil +end + +--- Returns the first value having specified keys `props`. +-- @name findWhere +-- @param t a table +-- @param props a set of keys +-- @return a value from the passed-in table +-- @see where +function M.findWhere(t, props) + local index = + M.detect( + t, + function(v) + for key in pairs(props) do + if props[key] ~= v[key] then + return false + end + end + return true + end + ) + return index and t[index] +end + +--- Selects and returns values passing an iterator test. +--
Aliased as `filter`. +-- @name select +-- @param t a table +-- @param f an iterator function, prototyped as `f (v, k)` +-- @return the selected values +-- @see reject +function M.select(t, f) + local _t = {} + for index, value in pairs(t) do + if f(value, index) then + _t[#_t + 1] = value + end + end + return _t +end + +--- Clones a table while dropping values passing an iterator test. +--
Aliased as `discard` +-- @name reject +-- @param t a table +-- @param f an iterator function, prototyped as `f (v, k)` +-- @return the remaining values +-- @see select +function M.reject(t, f) + local _t = {} + for index, value in pairs(t) do + if not f(value, index) then + _t[#_t + 1] = value + end + end + return _t +end + +--- Checks if all values in a table are passing an iterator test. +--
Aliased as `every` +-- @name all +-- @param t a table +-- @param f an iterator function, prototyped as `f (v, k)` +-- @return `true` if all values passes the predicate, `false` otherwise +function M.all(t, f) + for index, value in pairs(t) do + if not f(value, index) then + return false + end + end + return true +end + +--- Invokes a method on each value in a table. +-- @name invoke +-- @param t a table +-- @param method a function, prototyped as `f (v, k)` +-- @return the result of the call `f (v, k)` +-- @see pluck +function M.invoke(t, method) + return M.map( + t, + function(v, k) + if (type(v) == "table") then + if v[method] then + if M.isCallable(v[method]) then + return v[method](v, k) + else + return v[method] + end + else + if M.isCallable(method) then + return method(v, k) + end + end + elseif M.isCallable(method) then + return method(v, k) + end + end + ) +end + +--- Extracts values in a table having a given key. +-- @name pluck +-- @param t a table +-- @param key a key, will be used to index in each value: `value[key]` +-- @return an array of values having the given key +function M.pluck(t, key) + local _t = {} + for k, v in pairs(t) do + if v[key] then + _t[#_t + 1] = v[key] + end + end + return _t +end + +--- Returns the max value in a collection. If a `transform` function is passed, it will +-- be used to evaluate values by which all objects will be sorted. +-- @name max +-- @param t a table +-- @param[opt] transform a transformation function, prototyped as `transform (v, k)`, defaults to @{identity} +-- @return the max value found +-- @see min +function M.max(t, transform) + return extract(t, f_max, transform) +end + +--- Returns the min value in a collection. If a `transform` function is passed, it will +-- be used to evaluate values by which all objects will be sorted. +-- @name min +-- @param t a table +-- @param[opt] transform a transformation function, prototyped as `transform (v, k)`, defaults to @{identity} +-- @return the min value found +-- @see max +function M.min(t, transform) + return extract(t, f_min, transform) +end + +--- Checks if two tables are the same. It compares if both tables features the same values, +-- but not necessarily at the same keys. +-- @name same +-- @param a a table +-- @param b another table +-- @return `true` or `false` +function M.same(a, b) + return M.all( + a, + function(v) + return M.include(b, v) + end + ) and + M.all( + b, + function(v) + return M.include(a, v) + end + ) +end + +--- Sorts a table, in-place. If a comparison function is given, it will be used to sort values. +-- @name sort +-- @param t a table +-- @param[opt] comp a comparison function prototyped as `comp (a, b)`, defaults to < operator. +-- @return the given table, sorted. +-- @see sortBy +function M.sort(t, comp) + t_sort(t, comp) + return t +end + +--- Iterates on values with respect to key order. Keys are sorted using `comp` function +-- which defaults to `math.min`. It returns upon each call a `key, value` pair. +-- @name sortedk +-- @param t a table +-- @param[opt] comp a comparison function. Defaults to `<` operator +-- @return an iterator function +-- @see sortedv +function M.sortedk(t, comp) + local keys = M.keys(t) + t_sort(keys, comp) + local i = 0 + return function() + i = i + 1 + return keys[i], t[keys[i]] + end +end + +--- Iterates on values with respect to values order. Values are sorted using `comp` function +-- which defaults to `math.min`. It returns upon each call a `key, value` pair. +-- @name sortedv +-- @param t a table +-- @param[opt] comp a comparison function. Defaults to `<` operator +-- @return an iterator function +-- @see sortedk +function M.sortedv(t, comp) + local keys = M.keys(t) + comp = comp or f_min + t_sort( + keys, + function(a, b) + return comp(t[a], t[b]) + end + ) + local i = 0 + return function() + i = i + 1 + return keys[i], t[keys[i]] + end +end + +--- Sorts a table in-place using a transform. Values are ranked in a custom order of the results of +-- running `transform (v)` on all values. `transform` may also be a string name property sort by. +-- `comp` is a comparison function. +-- @name sortBy +-- @param t a table +-- @param[opt] transform a `transform` function to sort elements prototyped as `transform (v)`. Defaults to @{identity} +-- @param[optchain] comp a comparison function, defaults to the `<` operator +-- @return a new array of sorted values +-- @see sort +function M.sortBy(t, transform, comp) + local f = transform or M.identity + if (type(transform) == "string") then + f = function(t) + return t[transform] + end + end + comp = comp or f_min + t_sort( + t, + function(a, b) + return comp(f(a), f(b)) + end + ) + return t +end + +--- Splits a table into subsets groups. +-- @name groupBy +-- @param t a table +-- @param iter an iterator function, prototyped as `iter (v, k)` +-- @return a table of subsets groups +function M.groupBy(t, iter) + local _t = {} + for k, v in pairs(t) do + local _key = iter(v, k) + if _t[_key] then + _t[_key][#_t[_key] + 1] = v + else + _t[_key] = {v} + end + end + return _t +end + +--- Groups values in a collection and counts them. +-- @name countBy +-- @param t a table +-- @param iter an iterator function, prototyped as `iter (v, k)` +-- @return a table of subsets groups names paired with their count +function M.countBy(t, iter) + local stats = {} + for i, v in pairs(t) do + local key = iter(v, i) + stats[key] = (stats[key] or 0) + 1 + end + return stats +end + +--- Counts the number of values in a collection. If being passed more than one argument +-- it will return the count of all passed-in arguments. +-- @name size +-- @param[opt] ... Optional variable number of arguments +-- @return a count +-- @see count +-- @see countf +function M.size(...) + local args = {...} + local arg1 = args[1] + return (type(arg1) == "table") and count(args[1]) or count(args) +end + +--- Checks if all the keys of `other` table exists in table `t`. It does not +-- compares values. The test is not commutative, i.e table `t` may contains keys +-- not existing in `other`. +-- @name containsKeys +-- @param t a table +-- @param other another table +-- @return `true` or `false` +-- @see sameKeys +function M.containsKeys(t, other) + for key in pairs(other) do + if not t[key] then + return false + end + end + return true +end + +--- Checks if both given tables have the same keys. It does not compares values. +-- @name sameKeys +-- @param tA a table +-- @param tB another table +-- @return `true` or `false` +-- @see containsKeys +function M.sameKeys(tA, tB) + for key in pairs(tA) do + if not tB[key] then + return false + end + end + for key in pairs(tB) do + if not tA[key] then + return false + end + end + return true +end + +--- Array functions +-- @section Array functions + +--- Samples `n` random values from an array. If `n` is not specified, returns a single element. +-- It uses internally @{shuffle} to shuffle the array before sampling values. If `seed` is passed, +-- it will be used for shuffling. +-- @name sample +-- @param array an array +-- @param[opt] n a number of elements to be sampled. Defaults to 1. +-- @param[optchain] seed an optional seed for shuffling +-- @return an array of selected values +-- @see sampleProb +function M.sample(array, n, seed) + n = n or 1 + if n == 0 then + return {} + end + if n == 1 then + if seed then + randomseed(seed) + end + return {array[random(1, #array)]} + end + return M.slice(M.shuffle(array, seed), 1, n) +end + +--- Return elements from a sequence with a given probability. It considers each value independently. +-- Providing a seed will result in deterministic sampling. Given the same seed it will return the same sample +-- every time. +-- @name sampleProb +-- @param array an array +-- @param prob a probability for each element in array to be selected +-- @param[opt] seed an optional seed for deterministic sampling +-- @return an array of selected values +-- @see sample +function M.sampleProb(array, prob, seed) + if seed then + randomseed(seed) + end + local t = {} + for k, v in ipairs(array) do + if random() < prob then + t[#t + 1] = v + end + end + return t +end + +--- Returns the n-top values satisfying a predicate. It takes a comparison function +-- `comp` used to sort array values, and then picks the top n-values. It leaves the original array untouched. +-- @name nsorted +-- @param array an array +-- @param[opt] n a number of values to retrieve. Defaults to 1. +-- @param[optchain] comp a comparison function. Defaults to `<` operator. +-- @return an array of top n values +function M.nsorted(array, n, comp) + comp = comp or f_min + n = n or 1 + local values, count = {}, 0 + for k, v in M.sortedv(array, comp) do + if count < n then + count = count + 1 + values[count] = v + end + end + return values +end + +--- Returns a shuffled copy of a given array. If a seed is provided, it will +-- be used to init the built-in pseudo random number generator (using `math.randomseed`). +-- @name shuffle +-- @param array an array +-- @param[opt] seed a seed +-- @return a shuffled copy of the given array +function M.shuffle(array, seed) + if seed then + randomseed(seed) + end + local _shuffled = {} + for index, value in ipairs(array) do + local randPos = floor(random() * index) + 1 + _shuffled[index] = _shuffled[randPos] + _shuffled[randPos] = value + end + return _shuffled +end + +--- Converts a list of arguments to an array. +-- @name pack +-- @param ... a list of arguments +-- @return an array of all passed-in args +function M.pack(...) + return {...} +end + +--- Looks for the first occurrence of a given value in an array. Returns the value index if found. +-- Uses @{isEqual} to compare values. +-- @name find +-- @param array an array of values +-- @param value a value to lookup for +-- @param[opt] from the index from where the search will start. Defaults to 1. +-- @return the index of the value if found in the array, `nil` otherwise. +-- @see detect +function M.find(array, value, from) + for i = from or 1, #array do + if M.isEqual(array[i], value) then + return i + end + end +end + +--- Returns an array where values are in reverse order. The passed-in array should not be sparse. +-- @name reverse +-- @param array an array +-- @return a reversed array +function M.reverse(array) + local _array = {} + for i = #array, 1, -1 do + _array[#_array + 1] = array[i] + end + return _array +end + +--- Replaces elements in a given array with a given value. In case `i` and `j` are given +-- it will only replaces values at indexes between `[i,j]`. In case `j` is greater than the array +-- size, it will append new values, increasing the array size. +-- @name fill +-- @param array an array +-- @param value a value +-- @param[opt] i the index from which to start replacing values. Defaults to 1. +-- @param[optchain] j the index where to stop replacing values. Defaults to the array size. +-- @return the original array with values changed +function M.fill(array, value, i, j) + j = j or M.size(array) + for i = i or 1, j do + array[i] = value + end + return array +end + +--- Returns an array of `n` zeros. +-- @name zeros +-- @param n a number +-- @return an array +-- @see ones +-- @see vector +function M.zeros(n) + return M.fill({}, 0, 1, n) +end + +--- Returns an array of `n` 1's. +-- @name ones +-- @param n a number +-- @return an array +-- @see zeros +-- @see vector +function M.ones(n) + return M.fill({}, 1, 1, n) +end + +--- Returns an array of `n` times a given value. +-- @name vector +-- @param value a value +-- @param n a number +-- @return an array +-- @see zeros +-- @see ones +function M.vector(value, n) + return M.fill({}, value, 1, n) +end + +--- Collects values from a given array. The passed-in array should not be sparse. +-- This function collects values as long as they satisfy a given predicate and returns on the first falsy test. +--
Aliased as `takeWhile` +-- @name selectWhile +-- @param array an array +-- @param f an iterator function prototyped as `f (v, k)` +-- @return a new table containing all values collected +-- @see dropWhile +function M.selectWhile(array, f) + local t = {} + for i, v in ipairs(array) do + if f(v, i) then + t[i] = v + else + break + end + end + return t +end + +--- Collects values from a given array. The passed-in array should not be sparse. +-- This function collects values as long as they do not satisfy a given predicate and returns on the first truthy test. +--
Aliased as `rejectWhile` +-- @name dropWhile +-- @param array an array +-- @param f an iterator function prototyped as `f (v, k)` +-- @return a new table containing all values collected +-- @see selectWhile +function M.dropWhile(array, f) + local _i + for i, v in ipairs(array) do + if not f(v, i) then + _i = i + break + end + end + if (_i == nil) then + return {} + end + return M.rest(array, _i) +end + +--- Returns the index at which a value should be inserted. This index is evaluated so +-- that it maintains the sort. If a comparison function is passed, it will be used to sort +-- values. +-- @name sortedIndex +-- @param array an array +-- @param the value to be inserted +-- @param[opt] comp an comparison function prototyped as `f (a, b)`, defaults to < operator. +-- @param[optchain] sort whether or not the passed-in array should be sorted +-- @return number the index at which the passed-in value should be inserted +function M.sortedIndex(array, value, comp, sort) + local _comp = comp or f_min + if (sort == true) then + t_sort(array, _comp) + end + for i = 1, #array do + if not _comp(array[i], value) then + return i + end + end + return #array + 1 +end + +--- Returns the index of the first occurrence of value in an array. +-- @name indexOf +-- @param array an array +-- @param value the value to search for +-- @return the index of the passed-in value +-- @see lastIndexOf +function M.indexOf(array, value) + for k = 1, #array do + if array[k] == value then + return k + end + end +end + +--- Returns the index of the last occurrence of value in an array. +-- @name lastIndexOf +-- @param array an array +-- @param value the value to search for +-- @return the index of the last occurrence of the passed-in value or __nil__ +-- @see indexOf +function M.lastIndexOf(array, value) + local key = M.indexOf(M.reverse(array), value) + if key then + return #array - key + 1 + end +end + +--- Returns the first index at which a predicate returns true. +-- @name findIndex +-- @param array an array +-- @param pred a predicate function prototyped as `pred (v, k)` +-- @return the index found or __nil__ +-- @see findLastIndex +function M.findIndex(array, pred) + for k = 1, #array do + if pred(array[k], k) then + return k + end + end +end + +--- Returns the last index at which a predicate returns true. +-- @name findLastIndex +-- @param array an array +-- @param pred a predicate function prototyped as `pred (k, v)` +-- @return the index found or __nil__ +-- @see findIndex +function M.findLastIndex(array, pred) + local key = M.findIndex(M.reverse(array), pred) + if key then + return #array - key + 1 + end +end + +--- Adds all passed-in values at the top of an array. The last elements will bubble to the +-- top of the given array. +-- @name addTop +-- @param array an array +-- @param ... a variable number of arguments +-- @return the passed-in array with new values added +-- @see prepend +-- @see push +function M.addTop(array, ...) + for k, v in ipairs({...}) do + t_insert(array, 1, v) + end + return array +end + +--- Adds all passed-in values at the top of an array. As opposed to @{addTop}, it preserves the order +-- of the passed-in elements. +-- @name prepend +-- @param array an array +-- @param ... a variable number of arguments +-- @return the passed-in array with new values added +-- @see addTop +-- @see push +function M.prepend(array, ...) + return M.append({...}, array) +end + +--- Pushes all passed-in values at the end of an array. +-- @name push +-- @param array an array +-- @param ... a variable number of arguments +-- @return the passed-in array with new added values +-- @see addTop +-- @see prepend +function M.push(array, ...) + local args = {...} + for k, v in ipairs({...}) do + array[#array + 1] = v + end + return array +end + +--- Removes and returns the values at the top of a given array. +--
Aliased as `pop` +-- @name shift +-- @param array an array +-- @param[opt] n the number of values to be popped. Defaults to 1. +-- @return the popped values +-- @see unshift +function M.shift(array, n) + n = min(n or 1, #array) + local ret = {} + for i = 1, n do + local retValue = array[1] + ret[#ret + 1] = retValue + t_remove(array, 1) + end + return unpack(ret) +end + +--- Removes and returns the values at the end of a given array. +-- @name unshift +-- @param array an array +-- @param[opt] n the number of values to be unshifted. Defaults to 1. +-- @return the values +-- @see shift +function M.unshift(array, n) + n = min(n or 1, #array) + local ret = {} + for i = 1, n do + local retValue = array[#array] + ret[#ret + 1] = retValue + t_remove(array) + end + return unpack(ret) +end + +--- Removes all provided values in a given array. +--
Aliased as `remove` +-- @name pull +-- @param array an array +-- @param ... a variable number of values to be removed from the array +-- @return the passed-in array with values removed +function M.pull(array, ...) + local values = {...} + for i = #array, 1, -1 do + local remval = false + for k, rmValue in ipairs(values) do + if (remval == false) then + if M.isEqual(array[i], rmValue) then + t_remove(array, i) + remval = true + end + end + end + end + return array +end + +--- Removes values at an index within the range `[start, finish]`. +--
Aliased as `rmRange`, `chop` +-- @name removeRange +-- @param array an array +-- @param[opt] start the lower bound index, defaults to the first index in the array. +-- @param[optchain] finish the upper bound index, defaults to the array length. +-- @return the passed-in array with values removed +function M.removeRange(array, start, finish) + start = start or 1 + finish = finish or #array + if start > finish then + error("start cannot be greater than finish.") + end + for i = finish, start, -1 do + t_remove(array, i) + end + return array +end + +--- Chunks together consecutive values. Values are chunked on the basis of the return +-- value of a provided predicate `f (v, k)`. Consecutive elements which return +-- the same value are chunked together. Leaves the first argument untouched if it is not an array. +-- @name chunk +-- @param array an array +-- @param f an iterator function prototyped as `f (v, k)`. Defaults to @{identity}. +-- @return a table of chunks (arrays) +-- @see zip +function M.chunk(array, f) + local ch, ck, prev, val = {}, 0 + f = f or M.identity + for k, v in ipairs(array) do + val = f(v, k) + ck = ((val ~= prev) and (ck + 1) or ck) + prev = (prev == nil) and val or prev + if not ch[ck] then + ch[ck] = {array[k]} + else + ch[ck][#ch[ck] + 1] = array[k] + end + prev = val + end + return ch +end + +--- Slices values indexed within `[start, finish]` range. +--
Aliased as `M.sub` +-- @name slice +-- @param array an array +-- @param[opt] start the lower bound index, defaults to the first index in the array. +-- @param[optchain] finish the upper bound index, defaults to the array length. +-- @return a new array of sliced values +function M.slice(array, start, finish) + local t = {} + for k = start or 1, finish or #array do + t[#t + 1] = array[k] + end + return t +end + +--- Returns the first N values in an array. +--
Aliased as `head`, `take` +-- @name first +-- @param array an array +-- @param[opt] n the number of values to be collected, defaults to 1. +-- @return a new array +-- @see initial +-- @see last +-- @see rest +function M.first(array, n) + n = n or 1 + local t = {} + for k = 1, n do + t[k] = array[k] + end + return t +end + +--- Returns all values in an array excluding the last N values. +-- @name initial +-- @param array an array +-- @param[opt] n the number of values to be left, defaults to the array length. +-- @return a new array +-- @see first +-- @see last +-- @see rest +function M.initial(array, n) + local l = #array + n = n and l - (min(n, l)) or l - 1 + local t = {} + for k = 1, n do + t[k] = array[k] + end + return t +end + +--- Returns the last N values in an array. +-- @name last +-- @param array an array +-- @param[opt] n the number of values to be collected, defaults to the array length. +-- @return a new array +-- @see first +-- @see initial +-- @see rest +function M.last(array, n) + local l = #array + n = n and l - min(n - 1, l - 1) or 2 + local t = {} + for k = n, l do + t[#t + 1] = array[k] + end + return t +end + +--- Returns all values after index. +--
Aliased as `tail` +-- @name rest +-- @param array an array +-- @param[opt] index an index, defaults to 1 +-- @return a new array +-- @see first +-- @see initial +-- @see last +function M.rest(array, index) + local t = {} + for k = index or 1, #array do + t[#t + 1] = array[k] + end + return t +end + +--- Returns the value at a given index. +-- @name nth +-- @param array an array +-- @param index an index +-- @return the value at the given index +function M.nth(array, index) + return array[index] +end + +--- Returns all truthy values (removes `falses` and `nils`). +-- @name compact +-- @param array an array +-- @return a new array +function M.compact(array) + local t = {} + for k, v in pairs(array) do + if v then + t[#t + 1] = v + end + end + return t +end + +--- Flattens a nested array. Passing `shallow` will only flatten at the first level. +-- @name flatten +-- @param array an array +-- @param[opt] shallow specifies the flattening depth. Defaults to `false`.` +-- @return a flattened array +function M.flatten(array, shallow) + shallow = shallow or false + local new_flattened + local _flat = {} + for key, value in ipairs(array) do + if type(value) == "table" then + new_flattened = shallow and value or M.flatten(value) + for k, item in ipairs(new_flattened) do + _flat[#_flat + 1] = item + end + else + _flat[#_flat + 1] = value + end + end + return _flat +end + +--- Returns values from an array not present in all passed-in args. +--
Aliased as `without` and `diff` +-- @name difference +-- @param array an array +-- @param another array +-- @return a new array +-- @see union +-- @see intersection +-- @see symmetricDifference +function M.difference(array, array2) + if not array2 then + return M.clone(array) + end + return M.select( + array, + function(value) + return not M.include(array2, value) + end + ) +end + +--- Returns the duplicate-free union of all passed in arrays. +-- @name union +-- @param ... a variable number of arrays arguments +-- @return a new array +-- @see difference +-- @see intersection +-- @see symmetricDifference +function M.union(...) + return M.unique(M.flatten({...})) +end + +--- Returns the intersection of all passed-in arrays. +-- Each value in the result is present in each of the passed-in arrays. +-- @name intersection +-- @param ... a variable number of array arguments +-- @return a new array +-- @see difference +-- @see union +-- @see symmetricDifference +function M.intersection(...) + local arg = {...} + local array = arg[1] + t_remove(arg, 1) + local _intersect = {} + for i, value in ipairs(array) do + if + M.all( + arg, + function(v) + return M.include(v, value) + end + ) + then + _intersect[#_intersect + 1] = value + end + end + return _intersect +end + +--- Checks if all passed in arrays are disjunct. +-- @name disjoint +-- @param ... a variable number of arrays +-- @return `true` if the intersection of all arrays is not empty, `false` otherwise. +-- @see intersection +function M.disjoint(...) + return (#M.intersection(...) == 0) +end + +--- Performs a symmetric difference. Returns values from `array` not present in `array2` and also values +-- from `array2` not present in `array`. +--
Aliased as `symdiff` +-- @name symmetricDifference +-- @param array an array +-- @param array2 another array +-- @return a new array +-- @see difference +-- @see union +-- @see intersection +function M.symmetricDifference(array, array2) + return M.difference(M.union(array, array2), M.intersection(array, array2)) +end + +--- Produces a duplicate-free version of a given array. +--
Aliased as `uniq` +-- @name unique +-- @param array an array +-- @return a new array, duplicate-free +-- @see isunique +-- @see duplicates +function M.unique(array) + local ret = {} + for i = 1, #array do + if not M.find(ret, array[i]) then + ret[#ret + 1] = array[i] + end + end + return ret +end + +--- Checks if a given array contains distinct values. Such an array is made of distinct elements, +-- which only occur once in this array. +--
Aliased as `isuniq` +-- @name isunique +-- @param array an array +-- @return `true` if the given array is unique, `false` otherwise. +-- @see unique +-- @see duplicates +function M.isunique(array) + return #array == #(M.unique(array)) +end + +--- Returns an array list of all duplicates in array. +-- @name duplicates +-- @param array an array +-- @return an array-list of duplicates +-- @see unique +function M.duplicates(array) + local dict = M.invert(array) + local dups = {} + for k, v in ipairs(array) do + if dict[v] ~= k and not M.find(dups, v) then + dups[#dups + 1] = v + end + end + return dups +end + +--- Merges values of each of the passed-in arrays in subsets. +-- Only values indexed with the same key in the given arrays are merged in the same subset. +--
Aliased as `transpose` +-- @name zip +-- @param ... a variable number of array arguments +-- @return a new array +-- @see zipWith +function M.zip(...) + local args = {...} + local n = + M.max( + args, + function(array) + return #array + end + ) + local _ans = {} + for i = 1, n do + if not _ans[i] then + _ans[i] = {} + end + for k, array in ipairs(args) do + if (array[i] ~= nil) then + _ans[i][#_ans[i] + 1] = array[i] + end + end + end + return _ans +end + +--- Merges values using a given function. +-- Only values indexed with the same key in the given arrays are merged in the same subset. +-- Function `f` is used to combine values. +--
Aliased as `transposeWith` +-- @name zipWith +-- @param f a function +-- @param ... a variable number of array arguments +-- @return a flat array of results +-- @see zip +function M.zipWith(f, ...) + local args = {...} + local n = + M.max( + args, + function(array) + return #array + end + ) + local _ans = {} + for i = 1, n do + _ans[i] = f(unpack(M.pluck(args, i))) + end + return _ans +end + +--- Clones array and appends values from another array. +-- @name append +-- @param array an array +-- @param other an array +-- @return a new array +function M.append(array, other) + local t = {} + for i, v in ipairs(array) do + t[i] = v + end + for i, v in ipairs(other) do + t[#t + 1] = v + end + return t +end + +--- Interleaves arrays. It returns a single array made of values from all +-- passed in arrays in their given order, interleaved. +-- @name interleave +-- @param ... a variable list of arrays +-- @return a new array +-- @see interpose +function M.interleave(...) + local args = {...} + local n = M.max(args, M.size) + local t = {} + for i = 1, n do + for k, array in ipairs(args) do + if array[i] then + t[#t + 1] = array[i] + end + end + end + return t +end + +--- Interposes value in-between consecutive pair of values in array. +--
Aliased as `intersperse` +-- @name interpose +-- @param array an array +-- @param value a value +-- @return a new array +-- @see interleave +function M.interpose(array, value) + for k = #array, 2, -1 do + t_insert(array, k, value) + end + return array +end + +--- Produces a flexible list of numbers. If one value is passed, will count from 1 to that value, +-- with a default step of 1 (or -1). If two values are passed, will count from the first one to the second one, +-- using a default step of 1 (or -1). A third value passed will be considered a step value. +-- @name range +-- @param[opt] from the initial value of the range +-- @param[optchain] to the final value of the range +-- @param[optchain] step the step of count. Defaults to 1 or -1. +-- @return a new array of numbers +function M.range(from, to, step) + if (from == nil) and (to == nil) and (step == nil) then + return {} + elseif (from ~= nil) and (to == nil) and (step == nil) then + from, to, step = signum(from), from, signum(from) + elseif (from ~= nil) and (to ~= nil) and (step == nil) then + step = signum(to - from) + end + local _ranged = {from} + local steps = max(floor((to - from) / step), 0) + for i = 1, steps do + _ranged[#_ranged + 1] = from + step * i + end + return _ranged +end + +--- Creates an array list of `n` values, repeated. +-- @name rep +-- @param value a value to be repeated +-- @param n the number of repetitions of value. +-- @return a new array of `n` values +function M.rep(value, n) + local ret = {} + for i = 1, n do + ret[i] = value + end + return ret +end + +--- Returns the powerset of array values. For instance, when given the set {1,2,3}, +-- returns `{{},{1},{2},{3},{1,2},{2,3},{1,3},{1,2,3}}`. +-- @name powerset +-- @param array an array +-- @return an array +function M.powerset(array) + local n = #array + local powerset = {} + for i, v in ipairs(array) do + for j = 1, #powerset do + local set = powerset[j] + t_insert(powerset, M.push(M.slice(set), v)) + end + t_insert(powerset, {v}) + end + t_insert(powerset, {}) + return powerset +end + +--- Iterator returning partitions of an array. It returns arrays of length `n` +-- made of values from the given array. If the last partition has lower elements than `n` and +-- `pad` is supplied, it will be adjusted to `n` of elements with `pad` value. +-- @name partition +-- @param array an array +-- @param[opt] n the size of partitions. Defaults to 1. +-- @param[optchain] pads a value to adjust the last subsequence to the `n` elements +-- @return an iterator function +-- @see overlapping +-- @see aperture +function M.partition(array, n, pad) + if n <= 0 then + return + end + return wrap( + function() + partgen(array, n or 1, yield, pad) + end + ) +end + +--- Iterator returning overlapping partitions of an array. +-- If the last subsequence has lower elements than `n` and `pad` is +-- supplied, it will be adjusted to `n` elements with `pad` value. +-- @name overlapping +-- @param array an array +-- @param[opt] n the size of partitions. Defaults to 2. +-- @param[optchain] pads a value to adjust the last subsequence to the `n` elements +-- @return an iterator function +-- @see partition +-- @see aperture +function M.overlapping(array, n, pad) + if n <= 1 then + return + end + return wrap( + function() + partgen2(array, n or 2, yield, pad) + end + ) +end + +--- Iterator returning sliding partitions of an array. +--
Aliased as `sliding` +-- @name aperture +-- @param array an array +-- @param[opt] n the size of partitions. Defaults to 2 (and then behaves like @{pairwise}) +-- @return an iterator function +-- @see partition +-- @see overlapping +-- @see pairwise +function M.aperture(array, n) + if n <= 1 then + return + end + return wrap( + function() + partgen3(array, n or 2, yield) + end + ) +end + +--- Iterator returning sliding pairs of an array. +-- @name pairwise +-- @param array an array +-- @return an iterator function +-- @see overlapping +function M.pairwise(array) + return M.aperture(array, 2) +end + +--- Iterator returning the permutations of an array. It returns arrays made of all values +-- from the passed-in array, with values permuted. +-- @name permutation +-- @param array an array +-- @return an iterator function +function M.permutation(array) + return wrap( + function() + permgen(array, #array, yield) + end + ) +end + +--- Concatenates values in a given array. Handles booleans as well. If `sep` string is +-- passed, it will be used as a separator. Passing `i` and `j` will result in concatenating +-- only values within `[i, j]` range. +--
Aliased as `join` +-- @name concat +-- @param array a given array +-- @param[opt] sep a separator string, defaults to the empty string `''`. +-- @param[optchain] i the starting index, defaults to 1. +-- @param[optchain] j the final index, defaults to the array length. +-- @return a string +function M.concat(array, sep, i, j) + return t_concat(M.map(array, tostring), sep, i, j) +end + +--- Returns all possible pairs built from given arrays. +-- @name xprod +-- @param array a first array +-- @param array2 a second array +-- @return an array list of all pairs +function M.xprod(array, array2) + local p = {} + for i, v1 in ipairs(array) do + for j, v2 in ipairs(array2) do + p[#p + 1] = {v1, v2} + end + end + return p +end + +--- Creates pairs from value and array. Value is always prepended to the pair. +-- @name xpairs +-- @param valua a value +-- @param array an array +-- @return an array list of all pairs +function M.xpairs(value, array) + local xpairs = {} + for k, v in ipairs(array) do + xpairs[k] = {value, v} + end + return xpairs +end + +--- Creates pairs from value and array. Value is always appended as the last item to the pair. +-- @name xpairsRight +-- @param valua a value +-- @param array an array +-- @return an array list of all pairs +function M.xpairsRight(value, array) + local xpairs = {} + for k, v in ipairs(array) do + xpairs[k] = {v, value} + end + return xpairs +end + +--- Returns the sum of array values. +-- @name sum +-- @param array a given array +-- @return the sum of array values +function M.sum(array) + local s = 0 + for k, v in ipairs(array) do + s = s + v + end + return s +end + +--- Returns the product of array values. +-- @name product +-- @param array a given array +-- @return the product of array values +function M.product(array) + local p = 1 + for k, v in ipairs(array) do + p = p * v + end + return p +end + +--- Returns the mean of an array of numbers. +--
Aliased as `average` +-- @name mean +-- @param array an array of numbers +-- @return a number +-- @see sum +-- @see product +-- @see median +function M.mean(array) + return M.sum(array) / (#array) +end + +--- Returns the median of an array of numbers. +-- @name median +-- @param array an array of numbers +-- @return a number +-- @see sum +-- @see product +-- @see mean +function M.median(array) + local t = M.sort(M.clone(array)) + local n = #t + if n == 0 then + return + elseif n == 1 then + return t[1] + end + local mid = ceil(n / 2) + return n % 2 == 0 and (t[mid] + t[mid + 1]) / 2 or t[mid] +end + +--- Utility functions +-- @section Utility functions + +--- The no operation function. +-- @name noop +-- @return nothing +function M.noop() + return +end + +--- Returns the passed-in value. This function is used internally +-- as a default iterator. +-- @name identity +-- @param value a value +-- @return the passed-in value +function M.identity(value) + return value +end + +--- Calls `f` with the supplied arguments. Returns the results of `f(...)`. +-- @name call +-- @param f a function +-- @param[opt] ... a vararg list of args to `f` +-- @return the result of `f(...)` call. +function M.call(f, ...) + return f(...) +end + +--- Creates a constant function which returns the same output on every call. +--
Aliased as `always` +-- @name constant +-- @param value a constant value +-- @return a constant function +function M.constant(value) + return function() + return value + end +end + +--- Returns a function which applies `specs` on args. This function produces an object having +-- the same structure than `specs` by mapping each property to the result of calling its +-- associated function with the supplied arguments +-- @name applySpec +-- @param specs a table +-- @return a function +function M.applySpec(specs) + return function(...) + local spec = {} + for i, f in pairs(specs) do + spec[i] = f(...) + end + return spec + end +end + +--- Threads `value` through a series of functions. If a function expects more than one args, +-- it can be specified using an array list, where the first item is the function and the following +-- are the remaining args neeeded. The value is used as the first input. +-- @name thread +-- @param value a value +-- @param ... a vararg list of functions or arrays +-- @return a value +-- @see threadRight +function M.thread(value, ...) + local state = value + local arg = {...} + for k, t in ipairs(arg) do + if type(t) == "function" then + state = t(state) + elseif type(t) == "table" then + local f = t[1] + t_remove(t, 1) + state = M.reduce(t, f, state) + end + end + return state +end + +--- Threads `value` through a series of functions. If a function expects more than one args, +-- it can be specified using an array list, where the first item is the function and the following +-- are the remaining args neeeded. The value is used as the last input. +-- @name threadRight +-- @param value a value +-- @param ... a vararg list of functions or arrays +-- @return a value +-- @see thread +function M.threadRight(value, ...) + local state = value + local arg = {...} + for k, t in ipairs(arg) do + if type(t) == "function" then + state = t(state) + elseif type(t) == "table" then + local f = t[1] + t_remove(t, 1) + t_insert(t, state) + state = M.reduce(t, f) + end + end + return state +end + +--- Returns a dispatching function. When called with arguments, this function invokes each of its functions +-- in the passed-in order and returns the results of the first non-nil evaluation. +-- @name dispatch +-- @param ... a vararg list of functions +-- @return a dispatch function +function M.dispatch(...) + local funcs = {...} + return function(...) + for k, f in ipairs(funcs) do + local r = {f(...)} + if #r > 0 then + return unpack(r) + end + end + end +end + +--- Memoizes a given function by caching the computed result. +-- Useful for speeding-up slow-running functions. +--
Aliased as `cache` +-- @name memoize +-- @param f a function +-- @return a new function +function M.memoize(f) + local _cache = setmetatable({}, {__mode = "kv"}) + return function(key) + if (_cache[key] == nil) then + _cache[key] = f(key) + end + return _cache[key] + end +end + +--- Builds a list from a seed value. Accepts an iterator function, which +-- returns either nil to stop iteration or two values : the value to add to the list +-- of results and the seed to be used in the next call to the iterator function. +-- @name unfold +-- @param f an iterator function +-- @param seed a seed value +-- @return an array of values +function M.unfold(f, seed) + local t, result = {} + while true do + result, seed = f(seed) + if result ~= nil then + t[#t + 1] = result + else + break + end + end + return t +end + +--- Returns a version of `f` that runs only once. Successive calls to `f` +-- will keep yielding the same output, no matter what the passed-in arguments are. +-- It can be used to initialize variables. +-- @name once +-- @param f a function +-- @return a new function +-- @see before +-- @see after +function M.once(f) + local _internal = 0 + local _args = {} + return function(...) + _internal = _internal + 1 + if _internal <= 1 then + _args = {...} + end + return f(unpack(_args)) + end +end + +--- Returns a version of `f` that will run no more than count times. Next calls will +-- keep yielding the results of the count-th call. +-- @name before +-- @param f a function +-- @param count a count +-- @return a new function +-- @see once +-- @see after +function M.before(f, count) + local _internal = 0 + local _args = {} + return function(...) + _internal = _internal + 1 + if _internal <= count then + _args = {...} + end + return f(unpack(_args)) + end +end + +--- Returns a version of `f` that runs on the `count-th` call. +-- Useful when dealing with asynchronous tasks. +-- @name after +-- @param f a function +-- @param count the number of calls before `f` will start running. +-- @return a new function +-- @see once +-- @see before +function M.after(f, count) + local _limit, _internal = count, 0 + return function(...) + _internal = _internal + 1 + if _internal >= _limit then + return f(...) + end + end +end + +--- Composes functions. Each passed-in function consumes the return value of the function that follows. +-- In math terms, composing the functions `f`, `g`, and `h` produces the function `f(g(h(...)))`. +-- @name compose +-- @param ... a variable number of functions +-- @return a new function +-- @see pipe +function M.compose(...) + -- See: https://github.com/Yonaba/Moses/pull/15#issuecomment-139038895 + local f = M.reverse {...} + return function(...) + local first, _temp = true + for i, func in ipairs(f) do + if first then + first = false + _temp = func(...) + else + _temp = func(_temp) + end + end + return _temp + end +end + +--- Pipes a value through a series of functions. In math terms, +-- given some functions `f`, `g`, and `h` in that order, it returns `f(g(h(value)))`. +-- @name pipe +-- @param value a value +-- @param ... a variable number of functions +-- @return the result of the composition of function calls. +-- @see compose +function M.pipe(value, ...) + return M.compose(...)(value) +end + +--- Returns the logical complement of a given function. For a given input, the returned +-- function will output `false` if the original function would have returned `true`, +-- and vice-versa. +-- @name complement +-- @param f a function +-- @return the logical complement of the given function `f`. +function M.complement(f) + return function(...) + return not f(...) + end +end + +--- Calls a sequence of passed-in functions with the same argument. +-- Returns a sequence of results. +--
Aliased as `juxt` +-- @name juxtapose +-- @param value a value +-- @param ... a variable number of functions +-- @return a list of results +function M.juxtapose(value, ...) + local res = {} + for i, func in ipairs({...}) do + res[i] = func(value) + end + return unpack(res) +end + +--- Wraps `f` inside of the `wrapper` function. It passes `f` as the first argument to `wrapper`. +-- This allows the wrapper to execute code before and after `f` runs, +-- adjust the arguments, and execute it conditionally. +-- @name wrap +-- @param f a function to be wrapped, prototyped as `f (...)` +-- @param wrapper a wrapper function, prototyped as `wrapper (f, ...)` +-- @return the results +function M.wrap(f, wrapper) + return function(...) + return wrapper(f, ...) + end +end + +--- Runs `iter` function `n` times. Collects the results of each run and returns them in an array. +-- @name times +-- @param iter an iterator function, prototyped as `iter (i)` +-- @param[opt] n the number of times `iter` should be called. Defaults to 1. +-- @return table an array of results +function M.times(iter, n) + local results = {} + for i = 1, (n or 1) do + results[i] = iter(i) + end + return results +end + +--- Binds `v` to be the first argument to `f`. Calling `f (...)` will result to `f (v, ...)`. +-- @name bind +-- @param f a function +-- @param v a value +-- @return a function +-- @see bind2 +-- @see bindn +-- @see bindall +function M.bind(f, v) + return function(...) + return f(v, ...) + end +end + +--- Binds `v` to be the second argument to `f`. Calling `f (a, ...)` will result to `f (a, v, ...)`. +-- @name bind2 +-- @param f a function +-- @param v a value +-- @return a function +-- @see bind +-- @see bindn +-- @see bindall +function M.bind2(f, v) + return function(t, ...) + return f(t, v, ...) + end +end + +--- Binds `...` to be the N-first arguments to function `f`. +-- Calling `f (a1, a2, ..., aN)` will result to `f (..., a1, a2, ...,aN)`. +-- @name bindn +-- @param f a function +-- @param ... a variable number of arguments +-- @return a function +-- @see bind +-- @see bind2 +-- @see bindall +function M.bindn(f, ...) + local args = {...} + return function(...) + return f(unpack(M.append(args, {...}))) + end +end + +--- Binds methods to object. As such, whenever any of these methods is invoked, it +-- always receives the object as its first argument. +-- @name bindall +-- @param obj an abject +-- @param ... a variable number of method names +-- @return the passed-in object with all methods bound to the object itself. +-- @see bind +-- @see bind2 +-- @see bindn +function M.bindall(obj, ...) + local methodNames = {...} + for i, methodName in ipairs(methodNames) do + local method = obj[methodName] + if method then + obj[methodName] = M.bind(method, obj) + end + end + return obj +end + +--- Returns a function which iterate over a set of conditions. It invokes each predicate, +-- passing it given values. It returns the value of the corresponding function of the first +-- predicate to return a non-nil value. +-- @name cond +-- @param conds an array list of predicate-function pairs +-- @return the result of invoking `f(...)` of the first predicate to return a non-nil value +function M.cond(conds) + return function(...) + for k, condset in ipairs(conds) do + if condset[1](...) then + return condset[2](...) + end + end + end +end + +--- Returns a validation function. Given a set of functions, the validation function evaluates +-- to `true` only when all its funcs returns `true`. +-- @name both +-- @param ... an array list of functions +-- @return `true` when all given funcs returns true with input, false otherwise +function M.both(...) + local funcs = {...} + return function(...) + for k, f in ipairs(funcs) do + if not f(...) then + return false + end + end + return true + end +end + +--- Returns a validation function. Given a set of functions, the validation function evaluates +-- to `true` when at least one of its funcs returns `true`. +-- @name either +-- @param ... an array list of functions +-- @return `true` when one of the given funcs returns `true` with input, `false` otherwise +function M.either(...) + local funcs = {...} + return function(...) + for k, f in ipairs(funcs) do + if f(...) then + return true + end + end + return false + end +end + +--- Returns a validation function. Given a set of functions, the validation function evaluates +-- to `true` when neither of its func return `true`. +-- @name neither +-- @param ... an array list of functions +-- @return `true` when neither of the given funcs returns `true` with input, `false` otherwise +function M.neither(...) + local funcs = {...} + return function(...) + for k, f in ipairs(funcs) do + if f(...) then + return false + end + end + return true + end +end + +--- Generates an unique ID for the current session. If given a string `template`, it +-- will use this template for output formatting. Otherwise, if `template` is a function, it +-- will evaluate `template (id)`. +--
Aliased as `uid`. +-- @name uniqueId +-- @param[opt] template either a string or a function template to format the ID +-- @return value an ID +function M.uniqueId(template) + unique_id_counter = unique_id_counter + 1 + if template then + if type(template) == "string" then + return template:format(unique_id_counter) + elseif type(template) == "function" then + return template(unique_id_counter) + end + end + return unique_id_counter +end + +--- Produces an iterator which repeatedly apply a function `f` onto an input. +-- Yields `value`, then `f(value)`, then `f(f(value))`, continuously. +--
Aliased as `iter`. +-- @name iterator +-- @param f a function +-- @param value an initial input to `f` +-- @param[opt] n the number of times the iterator should run +-- @return an iterator function +function M.iterator(f, value, n) + local cnt = 0 + return function() + cnt = cnt + 1 + if n and cnt > n then + return + end + value = f(value) + return value + end +end + +--- Consumes the first `n` values of a iterator then returns it. +-- @name skip +-- @param iter an iterator function +-- @param[opt] n a number. Defaults to 1. +-- @return the given iterator +function M.skip(iter, n) + for i = 1, (n or 1) do + if iter() == nil then + return + end + end + return iter +end + +--- Iterates over an iterator and returns its values in an array. +-- @name tabulate +-- @param ... an iterator function (returning a generator, a state and a value) +-- @return an array of results +function M.tabulate(...) + local r = {} + for v in ... do + r[#r + 1] = v + end + return r +end + +--- Returns the length of an iterator. It consumes the iterator itself. +-- @name iterlen +-- @param ... an iterator function (returning a generator, a state and a value) +-- @return the iterator length +function M.iterlen(...) + local l = 0 + for v in ... do + l = l + 1 + end + return l +end + +--- Casts value as an array if it is not one. +-- @name castArray +-- @param value a value +-- @return an array containing the given value +function M.castArray(value) + return (type(value) ~= "table") and {value} or value +end + +--- Creates a function of `f` with arguments flipped in reverse order. +-- @name flip +-- @param f a function +-- @return a function +function M.flip(f) + return function(...) + return f(unpack(M.reverse({...}))) + end +end + +--- Returns a function that gets the nth argument. +-- If n is negative, the nth argument from the end is returned. +-- @name nthArg +-- @param n a number +-- @return a function +function M.nthArg(n) + return function(...) + local args = {...} + return args[(n < 0) and (#args + n + 1) or n] + end +end + +--- Returns a function which accepts up to one arg. It ignores any additional arguments. +-- @name unary +-- @param f a function +-- @return a function +-- @see ary +function M.unary(f) + return function(...) + local args = {...} + return f(args[1]) + end +end + +--- Returns a function which accepts up to `n` args. It ignores any additional arguments. +--
Aliased as `nAry`. +-- @name ary +-- @param f a function +-- @param[opt] n a number. Defaults to 1. +-- @return a function +-- @see unary +function M.ary(f, n) + n = n or 1 + return function(...) + local args = {...} + local fargs = {} + for i = 1, n do + fargs[i] = args[i] + end + return f(unpack(fargs)) + end +end + +--- Returns a function with an arity of 0. The new function ignores any arguments passed to it. +-- @name noarg +-- @param f a function +-- @return a new function +function M.noarg(f) + return function() + return f() + end +end + +--- Returns a function which runs with arguments rearranged. Arguments are passed to the +-- returned function in the order of supplied `indexes` at call-time. +-- @name rearg +-- @param f a function +-- @param indexes an array list of indexes +-- @return a function +function M.rearg(f, indexes) + return function(...) + local args = {...} + local reargs = {} + for i, arg in ipairs(indexes) do + reargs[i] = args[arg] + end + return f(unpack(reargs)) + end +end + +--- Creates a function that runs transforms on all arguments it receives. +-- @name over +-- @param ... a set of functions which will receive all arguments to the returned function +-- @return a function +-- @see overEvery +-- @see overSome +-- @see overArgs +function M.over(...) + local transforms = {...} + return function(...) + local r = {} + for i, transform in ipairs(transforms) do + r[#r + 1] = transform(...) + end + return r + end +end + +--- Creates a validation function. The returned function checks if *all* of the given predicates return +-- truthy when invoked with the arguments it receives. +-- @name overEvery +-- @param ... a list of predicate functions +-- @return a new function +-- @see over +-- @see overSome +-- @see overArgs +function M.overEvery(...) + local f = M.over(...) + return function(...) + return M.reduce( + f(...), + function(state, v) + return state and v + end + ) + end +end + +--- Creates a validation function. The return function checks if *any* of a given predicates return +-- truthy when invoked with the arguments it receives. +-- @name overSome +-- @param ... a list of predicate functions +-- @return a new function +-- @see over +-- @see overEvery +-- @see overArgs +function M.overSome(...) + local f = M.over(...) + return function(...) + return M.reduce( + f(...), + function(state, v) + return state or v + end + ) + end +end + +--- Creates a function that invokes `f` with its arguments transformed. 1rst arguments will be passed to +-- the 1rst transform, 2nd arg to the 2nd transform, etc. Remaining arguments will not be transformed. +-- @name overArgs +-- @param f a function +-- @param ... a list of transforms funcs prototyped as `f (v)` +-- @return the result of running `f` with its transformed arguments +-- @see over +-- @see overEvery +-- @see overSome +function M.overArgs(f, ...) + local _argf = {...} + return function(...) + local _args = {...} + for i = 1, #_argf do + local func = _argf[i] + if _args[i] then + _args[i] = func(_args[i]) + end + end + return f(unpack(_args)) + end +end + +--- Converges two functions into one. +-- @name converge +-- @param f a function +-- @param g a function +-- @param h a function +-- @return a new version of function f +function M.converge(f, g, h) + return function(...) + return f(g(...), h(...)) + end +end + +--- Partially apply a function by filling in any number of its arguments. +-- One may pass a string `'M'` as a placeholder in the list of arguments to specify an argument +-- that should not be pre-filled, but left open to be supplied at call-time. +-- @name partial +-- @param f a function +-- @param ... a list of partial arguments to `f` +-- @return a new version of function f having some of it original arguments filled +-- @see partialRight +-- @see curry +function M.partial(f, ...) + local partial_args = {...} + return function(...) + local n_args = {...} + local f_args = {} + for k, v in ipairs(partial_args) do + f_args[k] = (v == "_") and M.shift(n_args) or v + end + return f(unpack(M.append(f_args, n_args))) + end +end + +--- Similar to @{partial}, but from the right. +-- @name partialRight +-- @param f a function +-- @param ... a list of partial arguments to `f` +-- @return a new version of function f having some of it original arguments filled +-- @see partialRight +-- @see curry +function M.partialRight(f, ...) + local partial_args = {...} + return function(...) + local n_args = {...} + local f_args = {} + for k = 1, #partial_args do + f_args[k] = (partial_args[k] == "_") and M.shift(n_args) or partial_args[k] + end + return f(unpack(M.append(n_args, f_args))) + end +end + +--- Curries a function. If the given function `f` takes multiple arguments, it returns another version of +-- `f` that takes a single argument (the first of the arguments to the original function) and returns a new +-- function that takes the remainder of the arguments and returns the result. +-- @name curry +-- @param f a function +-- @param[opt] n_args the number of arguments expected for `f`. Defaults to 2. +-- @return a curried version of `f` +-- @see partial +-- @see partialRight +function M.curry(f, n_args) + n_args = n_args or 2 + local _args = {} + local function scurry(v) + if n_args == 1 then + return f(v) + end + if v ~= nil then + _args[#_args + 1] = v + end + if #_args < n_args then + return scurry + else + local r = {f(unpack(_args))} + _args = {} + return unpack(r) + end + end + return scurry +end + +--- Returns the execution time of `f (...)` and its returned values. +-- @name time +-- @param f a function +-- @param[opt] ... optional args to `f` +-- @return the execution time and the results of `f (...)` +function M.time(f, ...) + local stime = clock() + local r = {f(...)} + return clock() - stime, unpack(r) +end + +--- Object functions +-- @section Object functions + +--- Returns the keys of the object properties. +-- @name keys +-- @param obj an object +-- @return an array +function M.keys(obj) + local keys = {} + for key in pairs(obj) do + keys[#keys + 1] = key + end + return keys +end + +--- Returns the values of the object properties. +-- @name values +-- @param obj an object +-- @return an array of values +function M.values(obj) + local values = {} + for key, value in pairs(obj) do + values[#values + 1] = value + end + return values +end + +--- Returns the value at a given path in an object. +-- Path is given as a vararg list of keys. +-- @name path +-- @param obj an object +-- @param ... a vararg list of keys +-- @return a value or nil +function M.path(obj, ...) + local value, path = obj, {...} + for i, p in ipairs(path) do + if (value[p] == nil) then + return + end + value = value[p] + end + return value +end + +--- Spreads object under property path onto provided object. +-- It is similar to @{flattenPath}, but removes object under the property path. +-- @name spreadPath +-- @param obj an object +-- @param ... a property path given as a vararg list +-- @return the passed-in object with changes +-- @see flattenPath +function M.spreadPath(obj, ...) + local path = {...} + for _, p in ipairs(path) do + if obj[p] then + for k, v in pairs(obj[p]) do + obj[k] = v + obj[p][k] = nil + end + end + end + return obj +end + +--- Flattens object under property path onto provided object. +-- It is similar to @{spreadPath}, but preserves object under the property path. +-- @name flattenPath +-- @param obj an object +-- @param ... a property path given as a vararg list +-- @return the passed-in object with changes +-- @see spreadPath +function M.flattenPath(obj, ...) + local path = {...} + for _, p in ipairs(path) do + if obj[p] then + for k, v in pairs(obj[p]) do + obj[k] = v + end + end + end + return obj +end + +--- Converts key-value pairs to an array-list of `[k, v]` pairs. +-- @name kvpairs +-- @param obj an object +-- @return an array list of key-value pairs +-- @see toObj +function M.kvpairs(obj) + local t = {} + for k, v in pairs(obj) do + t[#t + 1] = {k, v} + end + return t +end + +--- Converts an array list of `[k,v]` pairs to an object. Keys are taken +-- from the 1rst column in the `[k,v]` pairs sequence, associated with values in the 2nd +-- column. +-- @name toObj +-- @param kvpairs an array-list of `[k,v]` pairs +-- @return an object +-- @see kvpairs +function M.toObj(kvpairs) + local obj = {} + for k, v in ipairs(kvpairs) do + obj[v[1]] = v[2] + end + return obj +end + +--- Swaps keys with values. Produces a new object where previous keys are now values, +-- while previous values are now keys. +--
Aliased as `mirror` +-- @name invert +-- @param obj a given object +-- @return a new object +function M.invert(obj) + local _ret = {} + for k, v in pairs(obj) do + _ret[v] = k + end + return _ret +end + +--- Returns a function that will return the key property of any passed-in object. +-- @name property +-- @param key a key property name +-- @return a function which should accept an object as argument +-- @see propertyOf +function M.property(key) + return function(obj) + return obj[key] + end +end + +--- Returns a function which will return the value of an object property. +-- @name propertyOf +-- @param obj an object +-- @return a function which should accept a key property argument +-- @see property +function M.propertyOf(obj) + return function(key) + return obj[key] + end +end + +--- Converts any given value to a boolean +-- @name toBoolean +-- @param value a value. Can be of any type +-- @return `true` if value is true, `false` otherwise (false or nil). +function M.toBoolean(value) + return not (not value) +end + +--- Extends an object properties. It copies the properties of extra passed-in objects +-- into the destination object, and returns the destination object. The last objects +-- will override properties of the same name. +-- @name extend +-- @param destObj a destination object +-- @param ... a list of objects +-- @return the destination object extended +function M.extend(destObj, ...) + local sources = {...} + for k, source in ipairs(sources) do + if type(source) == "table" then + for key, value in pairs(source) do + destObj[key] = value + end + end + end + return destObj +end + +--- Returns a sorted list of all methods names found in an object. If the given object +-- has a metatable implementing an `__index` field pointing to another table, will also recurse on this +-- table if `recurseMt` is provided. If `obj` is omitted, it defaults to the library functions. +--
Aliased as `methods`. +-- @name functions +-- @param[opt] obj an object. Defaults to Moses library functions. +-- @return an array-list of methods names +function M.functions(obj, recurseMt) + obj = obj or M + local _methods = {} + for key, value in pairs(obj) do + if type(value) == "function" then + _methods[#_methods + 1] = key + end + end + if recurseMt then + local mt = getmetatable(obj) + if mt and mt.__index then + local mt_methods = M.functions(mt.__index, recurseMt) + for k, fn in ipairs(mt_methods) do + _methods[#_methods + 1] = fn + end + end + end + return _methods +end + +--- Clones a given object properties. If `shallow` is passed will also clone nested array properties. +-- @name clone +-- @param obj an object +-- @param[opt] shallow whether or not nested array-properties should be cloned, defaults to false. +-- @return a copy of the passed-in object +function M.clone(obj, shallow) + if type(obj) ~= "table" then + return obj + end + local _obj = {} + for i, v in pairs(obj) do + if type(v) == "table" then + if not shallow then + _obj[i] = M.clone(v, shallow) + else + _obj[i] = v + end + else + _obj[i] = v + end + end + return _obj +end + +--- Invokes interceptor with the object, and then returns object. +-- The primary purpose of this method is to "tap into" a method chain, in order to perform operations +-- on intermediate results within the chain. +-- @name tap +-- @param obj an object +-- @param f an interceptor function, should be prototyped as `f (obj)` +-- @return the passed-in object +function M.tap(obj, f) + f(obj) + return obj +end + +--- Checks if a given object implements a property. +-- @name has +-- @param obj an object +-- @param key a key property to be checked +-- @return `true` or `false` +function M.has(obj, key) + return obj[key] ~= nil +end + +--- Returns an object copy having white-listed properties. +--
Aliased as `choose`. +-- @name pick +-- @param obj an object +-- @param ... a variable number of string keys +-- @return the filtered object +function M.pick(obj, ...) + local whitelist = M.flatten {...} + local _picked = {} + for key, property in pairs(whitelist) do + if (obj[property]) ~= nil then + _picked[property] = obj[property] + end + end + return _picked +end + +--- Returns an object copy without black-listed properties. +--
Aliased as `drop`. +-- @name omit +-- @param obj an object +-- @param ... a variable number of string keys +-- @return the filtered object +function M.omit(obj, ...) + local blacklist = M.flatten {...} + local _picked = {} + for key, value in pairs(obj) do + if not M.include(blacklist, key) then + _picked[key] = value + end + end + return _picked +end + +--- Applies a template to an object, preserving non-nil properties. +--
Aliased as `defaults`. +-- @name template +-- @param obj an object +-- @param[opt] template a template object. If `nil`, leaves `obj` untouched. +-- @return the passed-in object filled +function M.template(obj, template) + if not template then + return obj + end + for i, v in pairs(template) do + if not obj[i] then + obj[i] = v + end + end + return obj +end + +--- Performs a deep comparison test between two objects. Can compare strings, functions +-- (by reference), nil, booleans. Compares tables by reference or by values. If `useMt` +-- is passed, the equality operator `==` will be used if one of the given objects has a +-- metatable implementing `__eq`. +--
Aliased as `M.compare`, `M.matches` +-- @name isEqual +-- @param objA an object +-- @param objB another object +-- @param[opt] useMt whether or not `__eq` should be used, defaults to false. +-- @return `true` or `false` +-- @see allEqual +function M.isEqual(objA, objB, useMt) + local typeObjA = type(objA) + local typeObjB = type(objB) + + if typeObjA ~= typeObjB then + return false + end + if typeObjA ~= "table" then + return (objA == objB) + end + + local mtA = getmetatable(objA) + local mtB = getmetatable(objB) + + if useMt then + if (mtA or mtB) and (mtA.__eq or mtB.__eq) then + return mtA.__eq(objA, objB) or mtB.__eq(objB, objA) or (objA == objB) + end + end + + if M.size(objA) ~= M.size(objB) then + return false + end + + local vB + for i, vA in pairs(objA) do + vB = objB[i] + if vB == nil or not M.isEqual(vA, vB, useMt) then + return false + end + end + + for i in pairs(objB) do + if objA[i] == nil then + return false + end + end + + return true +end + +--- Invokes an object method. It passes the object itself as the first argument. if `method` is not +-- callable, will return `obj[method]`. +-- @name result +-- @param obj an object +-- @param method a string key to index in object `obj`. +-- @return the returned value of `method (obj)` call +function M.result(obj, method) + if obj[method] then + if M.isCallable(obj[method]) then + return obj[method](obj) + else + return obj[method] + end + end + if M.isCallable(method) then + return method(obj) + end +end + +--- Checks if the given arg is a table. +-- @name isTable +-- @param t a value to be tested +-- @return `true` or `false` +function M.isTable(t) + return type(t) == "table" +end + +--- Checks if the given argument is callable. Assumes `obj` is callable if +-- it is either a function or a table having a metatable implementing `__call` metamethod. +-- @name isCallable +-- @param obj an object +-- @return `true` or `false` +function M.isCallable(obj) + return ((type(obj) == "function") or + ((type(obj) == "table") and getmetatable(obj) and getmetatable(obj).__call ~= nil) or + false) +end + +--- Checks if the given argument is an array. Assumes `obj` is an array +-- if is a table with consecutive integer keys starting at 1. +-- @name isArray +-- @param obj an object +-- @return `true` or `false` +function M.isArray(obj) + if not (type(obj) == "table") then + return false + end + -- Thanks @Wojak and @Enrique García Cota for suggesting this + -- See : http://love2d.org/forums/viewtopic.php?f=3&t=77255&start=40#p163624 + local i = 0 + for k in pairs(obj) do + i = i + 1 + if obj[i] == nil then + return false + end + end + return true +end + +--- Checks if the given object is iterable with `pairs` (or `ipairs`). +-- @name isIterable +-- @param obj an object +-- @return `true` if the object can be iterated with `pairs` (or `ipairs`), `false` otherwise +function M.isIterable(obj) + return M.toBoolean((pcall(pairs, obj))) +end + +--- Extends Lua's `type` function. It returns the type of the given object and also recognises +-- file userdata +-- @name type +-- @param obj an object +-- @return the given object type +function M.type(obj) + local tp = type(obj) + if tp == "userdata" then + local mt = getmetatable(obj) + local stdout = io and io.stdout or nil + if stdout ~= nil and mt == getmetatable(stdout) then + return "file" + end + end + return tp +end + +--- Checks if the given pbject is empty. If `obj` is a string, will return `true` +-- if `#obj == 0`. Otherwise, if `obj` is a table, will return whether or not this table +-- is empty. If `obj` is `nil`, it will return true. +-- @name isEmpty +-- @param[opt] obj an object +-- @return `true` or `false` +function M.isEmpty(obj) + if (obj == nil) then + return true + end + if type(obj) == "string" then + return #obj == 0 + end + if type(obj) == "table" then + return next(obj) == nil + end + return true +end + +--- Checks if the given argument is a string. +-- @name isString +-- @param obj an object +-- @return `true` or `false` +function M.isString(obj) + return type(obj) == "string" +end + +--- Checks if the given argument is a function. +-- @name isFunction +-- @param obj an object +-- @return `true` or `false` +function M.isFunction(obj) + return type(obj) == "function" +end + +--- Checks if the given argument is nil. +-- @name isNil +-- @param obj an object +-- @return `true` or `false` +function M.isNil(obj) + return obj == nil +end + +--- Checks if the given argument is a number. +-- @name isNumber +-- @param obj an object +-- @return `true` or `false` +-- @see isNaN +function M.isNumber(obj) + return type(obj) == "number" +end + +--- Checks if the given argument is NaN (see [Not-A-Number](http://en.wikipedia.org/wiki/NaN)). +-- @name isNaN +-- @param obj an object +-- @return `true` or `false` +-- @see isNumber +function M.isNaN(obj) + return type(obj) == "number" and obj ~= obj +end + +--- Checks if the given argument is a finite number. +-- @name isFinite +-- @param obj an object +-- @return `true` or `false` +function M.isFinite(obj) + if type(obj) ~= "number" then + return false + end + return obj > -huge and obj < huge +end + +--- Checks if the given argument is a boolean. +-- @name isBoolean +-- @param obj an object +-- @return `true` or `false` +function M.isBoolean(obj) + return type(obj) == "boolean" +end + +--- Checks if the given argument is an integer. +-- @name isInteger +-- @param obj an object +-- @return `true` or `false` +function M.isInteger(obj) + return type(obj) == "number" and floor(obj) == obj +end + +-- Aliases + +do + -- Table functions aliases + M.forEach = M.each + M.forEachi = M.eachi + M.update = M.adjust + M.alleq = M.allEqual + M.loop = M.cycle + M.collect = M.map + M.inject = M.reduce + M.foldl = M.reduce + M.injectr = M.reduceRight + M.foldr = M.reduceRight + M.mapr = M.mapReduce + M.maprr = M.mapReduceRight + M.any = M.include + M.some = M.include + M.contains = M.include + M.filter = M.select + M.discard = M.reject + M.every = M.all + + -- Array functions aliases + M.takeWhile = M.selectWhile + M.rejectWhile = M.dropWhile + M.pop = M.shift + M.remove = M.pull + M.rmRange = M.removeRange + M.chop = M.removeRange + M.sub = M.slice + M.head = M.first + M.take = M.first + M.tail = M.rest + M.without = M.difference + M.diff = M.difference + M.symdiff = M.symmetricDifference + M.xor = M.symmetricDifference + M.uniq = M.unique + M.isuniq = M.isunique + M.transpose = M.zip + M.part = M.partition + M.perm = M.permutation + M.transposeWith = M.zipWith + M.intersperse = M.interpose + M.sliding = M.aperture + M.mirror = M.invert + M.join = M.concat + M.average = M.mean + + -- Utility functions aliases + M.always = M.constant + M.cache = M.memoize + M.juxt = M.juxtapose + M.uid = M.uniqueId + M.iter = M.iterator + M.nAry = M.ary + + -- Object functions aliases + M.methods = M.functions + M.choose = M.pick + M.drop = M.omit + M.defaults = M.template + M.compare = M.isEqual + M.matches = M.isEqual +end + +-- Setting chaining and building interface + +do + -- Wrapper to Moses + local f = {} + + -- Will be returned upon requiring, indexes into the wrapper + local Moses = {} + Moses.__index = f + + -- Wraps a value into an instance, and returns the wrapped object + local function new(value) + return setmetatable({_value = value, _wrapped = true}, Moses) + end + + setmetatable( + Moses, + { + __call = function(self, v) + return new(v) + end, -- Calls returns to instantiation + __index = function(t, key, ...) + return f[key] + end -- Redirects to the wrapper + } + ) + + --- Returns a wrapped object. Calling library functions as methods on this object + -- will continue to return wrapped objects until @{obj:value} is used. Can be aliased as `M(value)`. + -- @class function + -- @name chain + -- @param value a value to be wrapped + -- @return a wrapped object + function Moses.chain(value) + return new(value) + end + + --- Extracts the value of a wrapped object. Must be called on an chained object (see @{chain}). + -- @class function + -- @name obj:value + -- @return the value previously wrapped + function Moses:value() + return self._value + end + + -- Register chaining methods into the wrapper + f.chain, f.value = Moses.chain, Moses.value + + -- Register all functions into the wrapper + for fname, fct in pairs(M) do + if fname ~= "operator" then -- Prevents from wrapping op functions + f[fname] = function(v, ...) + local wrapped = type(v) == "table" and rawget(v, "_wrapped") or false + if wrapped then + local _arg = v._value + local _rslt = fct(_arg, ...) + return new(_rslt) + else + return fct(v, ...) + end + end + end + end + + -- Exports all op functions + f.operator = M.operator + f.op = M.operator + + --- Imports all library functions into a context. + -- @name import + -- @param[opt] context a context. Defaults to `_ENV or `_G`` (current environment). + -- @param[optchain] noConflict if supplied, will not import conflicting functions in the destination context. + -- @return the passed-in context + f.import = function(context, noConflict) + context = context or _ENV or _G + local funcs = M.functions() + for k, fname in ipairs(funcs) do + if rawget(context, fname) ~= nil then + if not noConflict then + rawset(context, fname, M[fname]) + end + else + rawset(context, fname, M[fname]) + end + end + return context + end + + -- Descriptive tags + Moses._VERSION = "Moses v" .. _MODULEVERSION + Moses._URL = "http://github.com/Yonaba/Moses" + Moses._LICENSE = "MIT " + Moses._DESCRIPTION = "utility-belt library for functional programming in Lua" + + return Moses +end diff --git a/framework/lualib/thirdparty/nesting/nesting.lua b/framework/lualib/thirdparty/nesting/nesting.lua new file mode 100644 index 0000000..e940d54 --- /dev/null +++ b/framework/lualib/thirdparty/nesting/nesting.lua @@ -0,0 +1,178 @@ +-- 嵌套表 +local class = class +local select = select +local next = next +local setmetatable = setmetatable +local assert = assert +local type = type +local getmetatable = getmetatable +local pairs = pairs +local xpcall = xpcall +local tostring = tostring +local table_insert = table.insert +-------------------------------------------------- +local NestingTable = class("NestingTable") + +-- 断言类型 +local function isKind(obj, strKind) + local mt + if type(obj) == "table" then + mt = getmetatable(obj) + end + + while mt do + if mt.__cname == strKind then + return true + end + mt = mt.super + end + + return false +end + +local function exception(e) + -- Log.e(e) + return e +end + +local function xxpcall(f, ...) + return xpcall(f, exception, ...) +end + +--- 构造函数 +-- @param deep 深度 +-- @param bWeak 弱引用 +function NestingTable:initialize(deep, bWeak) + self.m_deep = deep or 1 + self.m_bWeak = bWeak == true + self.m_count = 0 + self.m_items = {} + + self:clear() +end + +--- 新增对象 +-- @param item 对象实例 +-- @param k 键值 +-- @param ... 多个键值 +function NestingTable:add(item, k, ...) + if not item or not k or select("#", k, ...) ~= self.m_deep then + -- Log.w("NestingTable add nil!") + return + end + + if ... then + local _item = self:get(k) + if not _item then + local deep = self.m_deep - 1 + _item = NestingTable.new(deep, self.m_bWeak) + self.m_items[k] = _item + end + _item:add(item, ...) + else + self.m_items[k] = item + -- Log.d("NestingTable add", k, item) + end +end + +--- 删除对象 +-- @param k 键值 +-- @param ... 多个键值 +function NestingTable:remove(k, ...) + if not k or select("#", k, ...) ~= self.m_deep then + -- Log.w("NestingTable remove k nil!") + return + end + + local _item = self:get(k) + if not ... then + -- Log.d("NestingTable remove", k, _item) + self.m_items[k] = nil + elseif _item then + _item:remove(...) + if not next(_item:getItems()) then + self.m_items[k] = nil + end + end +end + +--- 取对象 +-- @param k 键值 +-- @param ... 多个键值 +-- @return 被管理对象实例 +function NestingTable:get(k, ...) + if not k then + -- Log.w("NestingTable get k nil!") + return + end + + local item = self.m_items[k] + if not ... then + return item + end + + if item then + return item:get(...) + end +end + +--- 取所有对象 +function NestingTable:getItems() + return self.m_items +end + +function NestingTable:clear() + if self.m_deep <= 1 then + self.m_items = self.m_bWeak and setmetatable({}, { + __mode = "v", + }) or {} + else + self.m_items = {} + end +end + +function NestingTable:traverse(closure) + assert(closure) + + for _, item in pairs(self.m_items) do + if isKind(item, "NestingTable") then + item:traverse(closure) + else + xxpcall(closure, item) + end + end +end + +function NestingTable:pack(packName, ...) + local ret = {} + + for key, item in pairs(self.m_items) do + if isKind(item, "NestingTable") then + ret[tostring(key)] = item:pack(packName, ...) + elseif packName then + ret[tostring(key)] = item[packName](item, ...) + else + ret[tostring(key)] = item + end + end + + return ret +end + +function NestingTable:packKey(deep) + assert(deep, "packKey") + + local ret = {} + + for key, item in pairs(self.m_items) do + if deep <= self.m_deep then + ret[tostring(key)] = item:packKey(deep) + else + table_insert(ret, key) + end + end + + return ret +end + +return NestingTable diff --git a/framework/lualib/thirdparty/oop/handler.lua b/framework/lualib/thirdparty/oop/handler.lua new file mode 100644 index 0000000..de7131e --- /dev/null +++ b/framework/lualib/thirdparty/oop/handler.lua @@ -0,0 +1,21 @@ +local type = type + +return function(method, obj, params) + return function(...) + if type(method) == "string" then + if params then + return obj[method](obj, params, ...) + else + return obj[method](obj, ...) + end + else + if obj and params then + return method(obj, params, ...) + elseif obj and not params then + return method(obj, ...) + else + return method(...) + end + end + end +end diff --git a/framework/lualib/thirdparty/oop/middleclass.lua b/framework/lualib/thirdparty/oop/middleclass.lua new file mode 100644 index 0000000..e90c7b8 --- /dev/null +++ b/framework/lualib/thirdparty/oop/middleclass.lua @@ -0,0 +1,212 @@ +local type = type +local pairs = pairs +local rawget = rawget +local setmetatable = setmetatable +local assert = assert +local tostring = tostring +local ipairs = ipairs +------------------------------------------------------------- + +local middleclass = { + _VERSION = 'middleclass v4.1.1', + _DESCRIPTION = 'Object Orientation for Lua', + _URL = 'https://github.com/kikito/middleclass', +} + +local function _createIndexWrapper(aClass, f) + if f == nil then + return aClass.__instanceDict + elseif type(f) == "function" then + return function(self, name) + local value = aClass.__instanceDict[name] + + if value ~= nil then + return value + else + return (f(self, name)) + end + end + else -- if type(f) == "table" then + return function(self, name) + local value = aClass.__instanceDict[name] + + if value ~= nil then + return value + else + return f[name] + end + end + end +end + +local function _propagateInstanceMethod(aClass, name, f) + f = name == "__index" and _createIndexWrapper(aClass, f) or f + aClass.__instanceDict[name] = f + + for subclass in pairs(aClass.subclasses) do + if rawget(subclass.__declaredMethods, name) == nil then + _propagateInstanceMethod(subclass, name, f) + end + end +end + +local function _declareInstanceMethod(aClass, name, f) + aClass.__declaredMethods[name] = f + + if f == nil and aClass.super then + f = aClass.super.__instanceDict[name] + end + + _propagateInstanceMethod(aClass, name, f) +end + +local function _tostring(self) + return "class " .. self.name +end +local function _call(self, ...) + return self:new(...) +end + +local function _createClass(name, super) + local dict = {} + dict.__index = dict + + local aClass = { + name = name, + super = super, + static = {}, + __instanceDict = dict, + __declaredMethods = {}, + subclasses = setmetatable({}, { + __mode = 'k', + }), + } + + if super then + setmetatable(aClass.static, { + __index = function(_, k) + local result = rawget(dict, k) + if result == nil then + return super.static[k] + end + return result + end, + }) + else + setmetatable(aClass.static, { + __index = function(_, k) + return rawget(dict, k) + end, + }) + end + + setmetatable(aClass, { + __index = aClass.static, + __tostring = _tostring, + __call = _call, + __newindex = _declareInstanceMethod, + }) + + return aClass +end + +local function _includeMixin(aClass, mixin) + assert(type(mixin) == 'table', "mixin must be a table") + + for name, method in pairs(mixin) do + if name ~= "included" and name ~= "static" then + aClass[name] = method + end + end + + for name, method in pairs(mixin.static or {}) do + aClass.static[name] = method + end + + if type(mixin.included) == "function" then + mixin:included(aClass) + end + return aClass +end + +local DefaultMixin = { + __tostring = function(self) + return "instance of " .. tostring(self.class) + end, + + initialize = function(self, ...) + end, + + isInstanceOf = function(self, aClass) + return type(aClass) == 'table' and type(self) == 'table' and + (self.class == aClass or type(self.class) == 'table' and + type(self.class.isSubclassOf) == 'function' and + self.class:isSubclassOf(aClass)) + end, + + static = { + allocate = function(self) + assert(type(self) == 'table', "Make sure that you are using 'Class:allocate' instead of 'Class.allocate'") + return setmetatable({ + class = self, + }, self.__instanceDict) + end, + + new = function(self, ...) + assert(type(self) == 'table', "Make sure that you are using 'Class:new' instead of 'Class.new'") + local instance = self:allocate() + instance:initialize(...) + return instance + end, + + subclass = function(self, name) + assert(type(self) == 'table', "Make sure that you are using 'Class:subclass' instead of 'Class.subclass'") + assert(type(name) == "string", "You must provide a name(string) for your class") + + local subclass = _createClass(name, self) + + for methodName, f in pairs(self.__instanceDict) do + if not (methodName == "__index" and type(f) == "table") then + _propagateInstanceMethod(subclass, methodName, f) + end + end + subclass.initialize = function(instance, ...) + return self.initialize(instance, ...) + end + + self.subclasses[subclass] = true + self:subclassed(subclass) + + return subclass + end, + + subclassed = function(self, other) + end, + + isSubclassOf = function(self, other) + return type(other) == 'table' and type(self.super) == 'table' and + (self.super == other or self.super:isSubclassOf(other)) + end, + + include = function(self, ...) + assert(type(self) == 'table', "Make sure you that you are using 'Class:include' instead of 'Class.include'") + for _, mixin in ipairs({...}) do + _includeMixin(self, mixin) + end + return self + end, + }, +} + +function middleclass.class(name, super) + assert(type(name) == 'string', "A name (string) is needed for the new class") + return super and super:subclass(name) or _includeMixin(_createClass(name), DefaultMixin) +end + +setmetatable(middleclass, { + __call = function(_, ...) + return middleclass.class(...) + end, +}) + +return middleclass diff --git a/framework/lualib/thirdparty/oop/option.lua b/framework/lualib/thirdparty/oop/option.lua new file mode 100755 index 0000000..0c91e22 --- /dev/null +++ b/framework/lualib/thirdparty/oop/option.lua @@ -0,0 +1,255 @@ +-- https://github.com/Sleitnick/LuaOption +--[[ + MatchTable { + Some: (value: any) -> any + None: () -> any + } + + CONSTRUCTORS: + + Option.Some(anyNonNilValue): Option + Option.Wrap(anyValue): Option + + + STATIC FIELDS: + + Option.None: Option + + + STATIC METHODS: + + Option.Is(obj): boolean + + + METHODS: + + opt:Match(): (matches: MatchTable) -> any + opt:IsSome(): boolean + opt:IsNone(): boolean + opt:Unwrap(): any + opt:Expect(errMsg: string): any + opt:ExpectNone(errMsg: string): void + opt:UnwrapOr(default: any): any + opt:UnwrapOrElse(default: () -> any): any + opt:And(opt2: Option): Option + opt:AndThen(predicate: (unwrapped: any) -> Option): Option + opt:Or(opt2: Option): Option + opt:OrElse(orElseFunc: () -> Option): Option + opt:XOr(opt2: Option): Option + opt:Contains(value: any): boolean + + -------------------------------------------------------------------- + + Options are useful for handling nil-value cases. Any time that an + operation might return nil, it is useful to instead return an + Option, which will indicate that the value might be nil, and should + be explicitly checked before using the value. This will help + prevent common bugs caused by nil values that can fail silently. + + + Example: + + local result1 = Option.Some(32) + local result2 = Option.Some(nil) + local result3 = Option.Some("Hi") + local result4 = Option.Some(nil) + local result5 = Option.None + + -- Use 'Match' to match if the value is Some or None: + result1:Match { + Some = function(value) print(value) end; + None = function() print("No value") end; + } + + -- Raw check: + if result2:IsSome() then + local value = result2:Unwrap() -- Explicitly call Unwrap + print("Value of result2:", value) + end + + if result3:IsNone() then + print("No result for result3") + end + + -- Bad, will throw error bc result4 is none: + local value = result4:Unwrap() + +--]] +local CLASSNAME = "Option" + +local Option = {} +Option.__index = Option + +function Option._new(value) + local self = setmetatable({ + ClassName = CLASSNAME, + _v = value, + _s = (value ~= nil), + }, Option) + return self +end + +function Option.Some(value) + assert(value ~= nil, "Option.Some() value cannot be nil") + return Option._new(value) +end + +function Option.Wrap(value) + if (value == nil) then + return Option.None + else + return Option.Some(value) + end +end + +function Option.Is(obj) + return (type(obj) == "table" and getmetatable(obj) == Option) +end + +function Option.Assert(obj) + assert(Option.Is(obj), "Result was not of type Option") +end + +function Option.Deserialize(data) -- type data = {ClassName: string, Value: any} + assert(type(data) == "table" and data.ClassName == CLASSNAME, "Invalid data for deserializing Option") + return (data.Value == nil and Option.None or Option.Some(data.Value)) +end + +function Option:Serialize() + return { + ClassName = self.ClassName, + Value = self._v, + } +end + +function Option:Match(matches) + local onSome = matches.Some + local onNone = matches.None + assert(type(onSome) == "function", "Missing 'Some' match") + assert(type(onNone) == "function", "Missing 'None' match") + if (self:IsSome()) then + return onSome(self:Unwrap()) + else + return onNone() + end +end + +function Option:IsSome() + return self._s +end + +function Option:IsNone() + return (not self._s) +end + +function Option:Expect(msg) + assert(self:IsSome(), msg) + return self._v +end + +function Option:ExpectNone(msg) + assert(self:IsNone(), msg) +end + +function Option:Unwrap() + return self:Expect("Cannot unwrap option of None type") +end + +function Option:UnwrapOr(default) + if (self:IsSome()) then + return self:Unwrap() + else + return default + end +end + +function Option:UnwrapOrElse(defaultFunc) + if (self:IsSome()) then + return self:Unwrap() + else + return defaultFunc() + end +end + +function Option:And(optB) + if (self:IsSome()) then + return optB + else + return Option.None + end +end + +function Option:AndThen(andThenFunc) + if (self:IsSome()) then + local result = andThenFunc(self:Unwrap()) + Option.Assert(result) + return result + else + return Option.None + end +end + +function Option:Or(optB) + if (self:IsSome()) then + return self + else + return optB + end +end + +function Option:OrElse(orElseFunc) + if (self:IsSome()) then + return self + else + local result = orElseFunc() + Option.Assert(result) + return result + end +end + +function Option:XOr(optB) + local someOptA = self:IsSome() + local someOptB = optB:IsSome() + if (someOptA == someOptB) then + return Option.None + elseif (someOptA) then + return self + else + return optB + end +end + +function Option:Filter(predicate) + if (self:IsNone() or not predicate(self._v)) then + return Option.None + else + return self + end +end + +function Option:Contains(value) + return (self:IsSome() and self._v == value) +end + +function Option:__tostring() + if (self:IsSome()) then + return ("Option<" .. type(self._v) .. ">") + else + return "Option" + end +end + +function Option:__eq(opt) + if (Option.Is(opt)) then + if (self:IsSome() and opt:IsSome()) then + return (self:Unwrap() == opt:Unwrap()) + elseif (self:IsNone() and opt:IsNone()) then + return true + end + end + return false +end + +Option.None = Option._new() + +return Option diff --git a/framework/lualib/thirdparty/oop/stateful.lua b/framework/lualib/thirdparty/oop/stateful.lua new file mode 100644 index 0000000..c02216b --- /dev/null +++ b/framework/lualib/thirdparty/oop/stateful.lua @@ -0,0 +1,216 @@ +local Stateful = { + _VERSION = 'Stateful 1.0.5 (2017-08)', + _DESCRIPTION = 'Stateful classes for middleclass', + _URL = 'https://github.com/kikito/stateful.lua', +} + +-- requires middleclass >2.0 +Stateful.static = {} + +local _callbacks = { + enteredState = 1, + exitedState = 1, + pushedState = 1, + poppedState = 1, + pausedState = 1, + continuedState = 1, +} + +local _BaseState = {} + +local function _addStatesToClass(klass, superStates) + klass.static.states = {} + for stateName, state in pairs(superStates or {}) do + klass:addState(stateName, state) + end +end + +local function _getStatefulMethod(instance, name) + if not _callbacks[name] then + local stack = rawget(instance, '__stateStack') + if not stack then + return + end + for i = #stack, 1, -1 do + if stack[i][name] then + return stack[i][name] + end + end + end +end + +local function _getNewInstanceIndex(prevIndex) + if type(prevIndex) == 'function' then + return function(instance, name) + return _getStatefulMethod(instance, name) or prevIndex(instance, name) + end + end + return function(instance, name) + return _getStatefulMethod(instance, name) or prevIndex[name] + end +end + +local function _getNewAllocateMethod(oldAllocateMethod) + return function(klass, ...) + local instance = oldAllocateMethod(klass, ...) + instance.__stateStack = {} + return instance + end +end + +local function _modifyInstanceIndex(klass) + klass.__instanceDict.__index = _getNewInstanceIndex(klass.__instanceDict.__index) +end + +local function _getNewSubclassMethod(prevSubclass) + return function(klass, name) + local subclass = prevSubclass(klass, name) + _addStatesToClass(subclass, klass.states) + _modifyInstanceIndex(subclass) + return subclass + end +end + +local function _modifySubclassMethod(klass) + klass.static.subclass = _getNewSubclassMethod(klass.static.subclass) +end + +local function _modifyAllocateMethod(klass) + klass.static.allocate = _getNewAllocateMethod(klass.static.allocate) +end + +local function _assertType(val, name, expected_type, type_to_s) + if type(val) ~= expected_type then + error("Expected " .. name .. " to be of type " .. (type_to_s or expected_type) .. ". Was " .. tostring(val) .. + "(" .. type(val) .. ")") + end +end + +local function _assertInexistingState(klass, stateName) + if klass.states[stateName] ~= nil then + error("State " .. tostring(stateName) .. " already exists on " .. tostring(klass)) + end +end + +local function _assertExistingState(self, state, stateName) + if not state then + error("The state " .. stateName .. " was not found in " .. tostring(self.class)) + end +end + +local function _invokeCallback(self, state, callbackName, ...) + if state and state[callbackName] then + state[callbackName](self, ...) + end +end + +local function _getCurrentState(self) + return self.__stateStack[#self.__stateStack] +end + +local function _getStateFromClassByName(self, stateName) + local state = self.class.static.states[stateName] + _assertExistingState(self, state, stateName) + return state +end + +local function _getStateIndexFromStackByName(self, stateName) + if stateName == nil then + return #self.__stateStack + end + local target = _getStateFromClassByName(self, stateName) + for i = #self.__stateStack, 1, -1 do + if self.__stateStack[i] == target then + return i + end + end +end + +local function _getStateName(self, target) + for name, state in pairs(self.class.static.states) do + if state == target then + return name + end + end +end + +function Stateful:included(klass) + _addStatesToClass(klass) + _modifyInstanceIndex(klass) + _modifySubclassMethod(klass) + _modifyAllocateMethod(klass) +end + +function Stateful.static:addState(stateName, superState) + superState = superState or _BaseState + _assertType(stateName, 'stateName', 'string') + _assertInexistingState(self, stateName) + self.static.states[stateName] = setmetatable({}, { + __index = superState, + }) + return self.static.states[stateName] +end + +function Stateful:gotoState(stateName, ...) + + self:popAllStates(...) + + if stateName == nil then + self.__stateStack = {} + else + _assertType(stateName, 'stateName', 'string', 'string or nil') + + local newState = _getStateFromClassByName(self, stateName) + self.__stateStack = {newState} + _invokeCallback(self, newState, 'enteredState', ...) + end +end + +function Stateful:pushState(stateName, ...) + local oldState = _getCurrentState(self) + _invokeCallback(self, oldState, 'pausedState') + + local newState = _getStateFromClassByName(self, stateName) + table.insert(self.__stateStack, newState) + + _invokeCallback(self, newState, 'pushedState', ...) + _invokeCallback(self, newState, 'enteredState', ...) +end + +function Stateful:popState(stateName, ...) + local oldStateIndex = _getStateIndexFromStackByName(self, stateName) + local oldState + if oldStateIndex then + oldState = self.__stateStack[oldStateIndex] + + _invokeCallback(self, oldState, 'poppedState', ...) + _invokeCallback(self, oldState, 'exitedState', ...) + + table.remove(self.__stateStack, oldStateIndex) + end + + local newState = _getCurrentState(self) + + if oldState ~= newState then + _invokeCallback(self, newState, 'continuedState', ...) + end +end + +function Stateful:popAllStates(...) + local size = #self.__stateStack + for _ = 1, size do + self:popState(nil, ...) + end +end + +function Stateful:getStateStackDebugInfo() + local info = {} + local state + for i = #self.__stateStack, 1, -1 do + state = self.__stateStack[i] + table.insert(info, _getStateName(self, state)) + end + return info +end + +return Stateful diff --git a/framework/lualib/thirdparty/pl/Date.lua b/framework/lualib/thirdparty/pl/Date.lua new file mode 100755 index 0000000..039dbaf --- /dev/null +++ b/framework/lualib/thirdparty/pl/Date.lua @@ -0,0 +1,675 @@ +--- Date and Date Format classes. +-- See @{05-dates.md|the Guide}. +-- +-- NOTE: the date module is deprecated! see +-- https://github.com/lunarmodules/Penlight/issues/285 +-- +-- Dependencies: `pl.class`, `pl.stringx`, `pl.utils` +-- @classmod pl.Date +-- @pragma nostrip + +local class = require 'pl.class' +local os_time, os_date = os.time, os.date +local stringx = require 'pl.stringx' +local utils = require 'pl.utils' +local assert_arg,assert_string = utils.assert_arg,utils.assert_string + + +utils.raise_deprecation { + source = "Penlight " .. utils._VERSION, + message = "the 'Date' module is deprecated, see https://github.com/lunarmodules/Penlight/issues/285", + version_removed = "2.0.0", + version_deprecated = "1.9.2", +} + + +local Date = class() +Date.Format = class() + +--- Date constructor. +-- @param t this can be either +-- +-- * `nil` or empty - use current date and time +-- * number - seconds since epoch (as returned by `os.time`). Resulting time is UTC +-- * `Date` - make a copy of this date +-- * table - table containing year, month, etc as for `os.time`. You may leave out year, month or day, +-- in which case current values will be used. +-- * year (will be followed by month, day etc) +-- +-- @param ... true if Universal Coordinated Time, or two to five numbers: month,day,hour,min,sec +-- @function Date +function Date:_init(t,...) + local time + local nargs = select('#',...) + if nargs > 2 then + local extra = {...} + local year = t + t = { + year = year, + month = extra[1], + day = extra[2], + hour = extra[3], + min = extra[4], + sec = extra[5] + } + end + if nargs == 1 then + self.utc = select(1,...) == true + end + if t == nil or t == 'utc' then + time = os_time() + self.utc = t == 'utc' + elseif type(t) == 'number' then + time = t + if self.utc == nil then self.utc = true end + elseif type(t) == 'table' then + if getmetatable(t) == Date then -- copy ctor + time = t.time + self.utc = t.utc + else + if not (t.year and t.month) then + local lt = os_date('*t') + if not t.year and not t.month and not t.day then + t.year = lt.year + t.month = lt.month + t.day = lt.day + else + t.year = t.year or lt.year + t.month = t.month or (t.day and lt.month or 1) + t.day = t.day or 1 + end + end + t.day = t.day or 1 + time = os_time(t) + end + else + error("bad type for Date constructor: "..type(t),2) + end + self:set(time) +end + +--- set the current time of this Date object. +-- @int t seconds since epoch +function Date:set(t) + self.time = t + if self.utc then + self.tab = os_date('!*t',t) + else + self.tab = os_date('*t',t) + end +end + +--- get the time zone offset from UTC. +-- @int ts seconds ahead of UTC +function Date.tzone (ts) + if ts == nil then + ts = os_time() + elseif type(ts) == "table" then + if getmetatable(ts) == Date then + ts = ts.time + else + ts = Date(ts).time + end + end + local utc = os_date('!*t',ts) + local lcl = os_date('*t',ts) + lcl.isdst = false + return os.difftime(os_time(lcl), os_time(utc)) +end + +--- convert this date to UTC. +function Date:toUTC () + local ndate = Date(self) + if not self.utc then + ndate.utc = true + ndate:set(ndate.time) + end + return ndate +end + +--- convert this UTC date to local. +function Date:toLocal () + local ndate = Date(self) + if self.utc then + ndate.utc = false + ndate:set(ndate.time) +--~ ndate:add { sec = Date.tzone(self) } + end + return ndate +end + +--- set the year. +-- @int y Four-digit year +-- @class function +-- @name Date:year + +--- set the month. +-- @int m month +-- @class function +-- @name Date:month + +--- set the day. +-- @int d day +-- @class function +-- @name Date:day + +--- set the hour. +-- @int h hour +-- @class function +-- @name Date:hour + +--- set the minutes. +-- @int min minutes +-- @class function +-- @name Date:min + +--- set the seconds. +-- @int sec seconds +-- @class function +-- @name Date:sec + +--- set the day of year. +-- @class function +-- @int yday day of year +-- @name Date:yday + +--- get the year. +-- @int y Four-digit year +-- @class function +-- @name Date:year + +--- get the month. +-- @class function +-- @name Date:month + +--- get the day. +-- @class function +-- @name Date:day + +--- get the hour. +-- @class function +-- @name Date:hour + +--- get the minutes. +-- @class function +-- @name Date:min + +--- get the seconds. +-- @class function +-- @name Date:sec + +--- get the day of year. +-- @class function +-- @name Date:yday + + +for _,c in ipairs{'year','month','day','hour','min','sec','yday'} do + Date[c] = function(self,val) + if val then + assert_arg(1,val,"number") + self.tab[c] = val + self:set(os_time(self.tab)) + return self + else + return self.tab[c] + end + end +end + +--- name of day of week. +-- @bool full abbreviated if true, full otherwise. +-- @ret string name +function Date:weekday_name(full) + return os_date(full and '%A' or '%a',self.time) +end + +--- name of month. +-- @int full abbreviated if true, full otherwise. +-- @ret string name +function Date:month_name(full) + return os_date(full and '%B' or '%b',self.time) +end + +--- is this day on a weekend?. +function Date:is_weekend() + return self.tab.wday == 1 or self.tab.wday == 7 +end + +--- add to a date object. +-- @param t a table containing one of the following keys and a value: +-- one of `year`,`month`,`day`,`hour`,`min`,`sec` +-- @return this date +function Date:add(t) + local old_dst = self.tab.isdst + local key,val = next(t) + self.tab[key] = self.tab[key] + val + self:set(os_time(self.tab)) + if old_dst ~= self.tab.isdst then + self.tab.hour = self.tab.hour - (old_dst and 1 or -1) + self:set(os_time(self.tab)) + end + return self +end + +--- last day of the month. +-- @return int day +function Date:last_day() + local d = 28 + local m = self.tab.month + while self.tab.month == m do + d = d + 1 + self:add{day=1} + end + self:add{day=-1} + return self +end + +--- difference between two Date objects. +-- @tparam Date other Date object +-- @treturn Date.Interval object +function Date:diff(other) + local dt = self.time - other.time + if dt < 0 then error("date difference is negative!",2) end + return Date.Interval(dt) +end + +--- long numerical ISO data format version of this date. +function Date:__tostring() + local fmt = '%Y-%m-%dT%H:%M:%S' + if self.utc then + fmt = "!"..fmt + end + local t = os_date(fmt,self.time) + if self.utc then + return t .. 'Z' + else + local offs = self:tzone() + if offs == 0 then + return t .. 'Z' + end + local sign = offs > 0 and '+' or '-' + local h = math.ceil(offs/3600) + local m = (offs % 3600)/60 + if m == 0 then + return t .. ('%s%02d'):format(sign,h) + else + return t .. ('%s%02d:%02d'):format(sign,h,m) + end + end +end + +--- equality between Date objects. +function Date:__eq(other) + return self.time == other.time +end + +--- ordering between Date objects. +function Date:__lt(other) + return self.time < other.time +end + +--- difference between Date objects. +-- @function Date:__sub +Date.__sub = Date.diff + +--- add a date and an interval. +-- @param other either a `Date.Interval` object or a table such as +-- passed to `Date:add` +function Date:__add(other) + local nd = Date(self) + if Date.Interval:class_of(other) then + other = {sec=other.time} + end + nd:add(other) + return nd +end + +Date.Interval = class(Date) + +---- Date.Interval constructor +-- @int t an interval in seconds +-- @function Date.Interval +function Date.Interval:_init(t) + self:set(t) +end + +function Date.Interval:set(t) + self.time = t + self.tab = os_date('!*t',self.time) +end + +local function ess(n) + if n > 1 then return 's ' + else return ' ' + end +end + +--- If it's an interval then the format is '2 hours 29 sec' etc. +function Date.Interval:__tostring() + local t, res = self.tab, '' + local y,m,d = t.year - 1970, t.month - 1, t.day - 1 + if y > 0 then res = res .. y .. ' year'..ess(y) end + if m > 0 then res = res .. m .. ' month'..ess(m) end + if d > 0 then res = res .. d .. ' day'..ess(d) end + if y == 0 and m == 0 then + local h = t.hour + if h > 0 then res = res .. h .. ' hour'..ess(h) end + if t.min > 0 then res = res .. t.min .. ' min ' end + if t.sec > 0 then res = res .. t.sec .. ' sec ' end + end + if res == '' then res = 'zero' end + return res +end + +------------ Date.Format class: parsing and renderinig dates ------------ + +-- short field names, explicit os.date names, and a mask for allowed field repeats +local formats = { + d = {'day',{true,true}}, + y = {'year',{false,true,false,true}}, + m = {'month',{true,true}}, + H = {'hour',{true,true}}, + M = {'min',{true,true}}, + S = {'sec',{true,true}}, +} + +--- Date.Format constructor. +-- @string fmt. A string where the following fields are significant: +-- +-- * d day (either d or dd) +-- * y year (either yy or yyy) +-- * m month (either m or mm) +-- * H hour (either H or HH) +-- * M minute (either M or MM) +-- * S second (either S or SS) +-- +-- Alternatively, if fmt is nil then this returns a flexible date parser +-- that tries various date/time schemes in turn: +-- +-- * [ISO 8601](http://en.wikipedia.org/wiki/ISO_8601), like `2010-05-10 12:35:23Z` or `2008-10-03T14:30+02` +-- * times like 15:30 or 8.05pm (assumed to be today's date) +-- * dates like 28/10/02 (European order!) or 5 Feb 2012 +-- * month name like march or Mar (case-insensitive, first 3 letters); here the +-- day will be 1 and the year this current year +-- +-- A date in format 3 can be optionally followed by a time in format 2. +-- Please see test-date.lua in the tests folder for more examples. +-- @usage df = Date.Format("yyyy-mm-dd HH:MM:SS") +-- @class function +-- @name Date.Format +function Date.Format:_init(fmt) + if not fmt then + self.fmt = '%Y-%m-%d %H:%M:%S' + self.outf = self.fmt + self.plain = true + return + end + local append = table.insert + local D,PLUS,OPENP,CLOSEP = '\001','\002','\003','\004' + local vars,used = {},{} + local patt,outf = {},{} + local i = 1 + while i < #fmt do + local ch = fmt:sub(i,i) + local df = formats[ch] + if df then + if used[ch] then error("field appeared twice: "..ch,4) end + used[ch] = true + -- this field may be repeated + local _,inext = fmt:find(ch..'+',i+1) + local cnt = not _ and 1 or inext-i+1 + if not df[2][cnt] then error("wrong number of fields: "..ch,4) end + -- single chars mean 'accept more than one digit' + local p = cnt==1 and (D..PLUS) or (D):rep(cnt) + append(patt,OPENP..p..CLOSEP) + append(vars,ch) + if ch == 'y' then + append(outf,cnt==2 and '%y' or '%Y') + else + append(outf,'%'..ch) + end + i = i + cnt + else + append(patt,ch) + append(outf,ch) + i = i + 1 + end + end + -- escape any magic characters + fmt = utils.escape(table.concat(patt)) + -- fmt = table.concat(patt):gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1') + -- replace markers with their magic equivalents + fmt = fmt:gsub(D,'%%d'):gsub(PLUS,'+'):gsub(OPENP,'('):gsub(CLOSEP,')') + self.fmt = fmt + self.outf = table.concat(outf) + self.vars = vars +end + +local parse_date + +--- parse a string into a Date object. +-- @string str a date string +-- @return date object +function Date.Format:parse(str) + assert_string(1,str) + if self.plain then + return parse_date(str,self.us) + end + local res = {str:match(self.fmt)} + if #res==0 then return nil, 'cannot parse '..str end + local tab = {} + for i,v in ipairs(self.vars) do + local name = formats[v][1] -- e.g. 'y' becomes 'year' + tab[name] = tonumber(res[i]) + end + -- os.date() requires these fields; if not present, we assume + -- that the time set is for the current day. + if not (tab.year and tab.month and tab.day) then + local today = Date() + tab.year = tab.year or today:year() + tab.month = tab.month or today:month() + tab.day = tab.day or today:day() + end + local Y = tab.year + if Y < 100 then -- classic Y2K pivot + tab.year = Y + (Y < 35 and 2000 or 1999) + elseif not Y then + tab.year = 1970 + end + return Date(tab) +end + +--- convert a Date object into a string. +-- @param d a date object, or a time value as returned by @{os.time} +-- @return string +function Date.Format:tostring(d) + local tm + local fmt = self.outf + if type(d) == 'number' then + tm = d + else + tm = d.time + if d.utc then + fmt = '!'..fmt + end + end + return os_date(fmt,tm) +end + +--- force US order in dates like 9/11/2001 +function Date.Format:US_order(yesno) + self.us = yesno +end + +--local months = {jan=1,feb=2,mar=3,apr=4,may=5,jun=6,jul=7,aug=8,sep=9,oct=10,nov=11,dec=12} +local months +local parse_date_unsafe +local function create_months() + local ld, day1 = parse_date_unsafe '2000-12-31', {day=1} + months = {} + for i = 1,12 do + ld = ld:last_day() + ld:add(day1) + local mon = ld:month_name():lower() + months [mon] = i + end +end + +--[[ +Allowed patterns: +- [day] [monthname] [year] [time] +- [day]/[month][/year] [time] + +]] + +local function looks_like_a_month(w) + return w:match '^%a+,*$' ~= nil +end +local is_number = stringx.isdigit +local function tonum(s,l1,l2,kind) + kind = kind or '' + local n = tonumber(s) + if not n then error(("%snot a number: '%s'"):format(kind,s)) end + if n < l1 or n > l2 then + error(("%s out of range: %s is not between %d and %d"):format(kind,s,l1,l2)) + end + return n +end + +local function parse_iso_end(p,ns,sec) + -- may be fractional part of seconds + local _,nfrac,secfrac = p:find('^%.%d+',ns+1) + if secfrac then + sec = sec .. secfrac + p = p:sub(nfrac+1) + else + p = p:sub(ns+1) + end + -- ISO 8601 dates may end in Z (for UTC) or [+-][isotime] + -- (we're working with the date as lower case, hence 'z') + if p:match 'z$' then -- we're UTC! + return sec, {h=0,m=0} + end + p = p:gsub(':','') -- turn 00:30 to 0030 + local _,_,sign,offs = p:find('^([%+%-])(%d+)') + if not sign then return sec, nil end -- not UTC + + if #offs == 2 then offs = offs .. '00' end -- 01 to 0100 + local tz = { h = tonumber(offs:sub(1,2)), m = tonumber(offs:sub(3,4)) } + if sign == '-' then tz.h = -tz.h; tz.m = -tz.m end + return sec, tz +end + +function parse_date_unsafe (s,US) + s = s:gsub('T',' ') -- ISO 8601 + local parts = stringx.split(s:lower()) + local i,p = 1,parts[1] + local function nextp() i = i + 1; p = parts[i] end + local year,min,hour,sec,apm + local tz + local _,nxt,day, month = p:find '^(%d+)/(%d+)' + if day then + -- swop for US case + if US then + day, month = month, day + end + _,_,year = p:find('^/(%d+)',nxt+1) + nextp() + else -- ISO + year,month,day = p:match('^(%d+)%-(%d+)%-(%d+)') + if year then + nextp() + end + end + if p and not year and is_number(p) then -- has to be date + if #p < 4 then + day = p + nextp() + else -- unless it looks like a 24-hour time + year = true + end + end + if p and looks_like_a_month(p) then -- date followed by month + p = p:sub(1,3) + if not months then + create_months() + end + local mon = months[p] + if mon then + month = mon + else error("not a month: " .. p) end + nextp() + end + if p and not year and is_number(p) then + year = p + nextp() + end + + if p then -- time is hh:mm[:ss], hhmm[ss] or H.M[am|pm] + _,nxt,hour,min = p:find '^(%d+):(%d+)' + local ns + if nxt then -- are there seconds? + _,ns,sec = p:find ('^:(%d+)',nxt+1) + --if ns then + sec,tz = parse_iso_end(p,ns or nxt,sec) + --end + else -- might be h.m + _,ns,hour,min = p:find '^(%d+)%.(%d+)' + if ns then + apm = p:match '[ap]m$' + else -- or hhmm[ss] + local hourmin + _,nxt,hourmin = p:find ('^(%d+)') + if nxt then + hour = hourmin:sub(1,2) + min = hourmin:sub(3,4) + sec = hourmin:sub(5,6) + if #sec == 0 then sec = nil end + sec,tz = parse_iso_end(p,nxt,sec) + end + end + end + end + local today + if year == true then year = nil end + if not (year and month and day) then + today = Date() + end + day = day and tonum(day,1,31,'day') or (month and 1 or today:day()) + month = month and tonum(month,1,12,'month') or today:month() + year = year and tonumber(year) or today:year() + if year < 100 then -- two-digit year pivot around year < 2035 + year = year + (year < 35 and 2000 or 1900) + end + hour = hour and tonum(hour,0,apm and 12 or 24,'hour') or 12 + if apm == 'pm' then + hour = hour + 12 + end + min = min and tonum(min,0,59) or 0 + sec = sec and tonum(sec,0,60) or 0 --60 used to indicate leap second + local res = Date {year = year, month = month, day = day, hour = hour, min = min, sec = sec} + if tz then -- ISO 8601 UTC time + local corrected = false + if tz.h ~= 0 then res:add {hour = -tz.h}; corrected = true end + if tz.m ~= 0 then res:add {min = -tz.m}; corrected = true end + res.utc = true + -- we're in UTC, so let's go local... + if corrected then + res = res:toLocal() + end-- we're UTC! + end + return res +end + +function parse_date (s) + local ok, d = pcall(parse_date_unsafe,s) + if not ok then -- error + d = d:gsub('.-:%d+: ','') + return nil, d + else + return d + end +end + +return Date + diff --git a/framework/lualib/thirdparty/pl/List.lua b/framework/lualib/thirdparty/pl/List.lua new file mode 100755 index 0000000..b66c251 --- /dev/null +++ b/framework/lualib/thirdparty/pl/List.lua @@ -0,0 +1,566 @@ +--- Python-style list class. +-- +-- **Please Note**: methods that change the list will return the list. +-- This is to allow for method chaining, but please note that `ls = ls:sort()` +-- does not mean that a new copy of the list is made. In-place (mutable) methods +-- are marked as returning 'the list' in this documentation. +-- +-- See the Guide for further @{02-arrays.md.Python_style_Lists|discussion} +-- +-- See http://www.python.org/doc/current/tut/tut.html, section 5.1 +-- +-- **Note**: The comments before some of the functions are from the Python docs +-- and contain Python code. +-- +-- Written for Lua version Nick Trout 4.0; Redone for Lua 5.1, Steve Donovan. +-- +-- Dependencies: `pl.utils`, `pl.tablex`, `pl.class` +-- @classmod pl.List +-- @pragma nostrip + +local tinsert,tremove,concat,tsort = table.insert,table.remove,table.concat,table.sort +local setmetatable, getmetatable,type,tostring,string = setmetatable,getmetatable,type,tostring,string +local tablex = require 'pl.tablex' +local filter,imap,imap2,reduce,transform,tremovevalues = tablex.filter,tablex.imap,tablex.imap2,tablex.reduce,tablex.transform,tablex.removevalues +local tsub = tablex.sub +local utils = require 'pl.utils' +local class = require 'pl.class' + +local array_tostring,split,assert_arg,function_arg = utils.array_tostring,utils.split,utils.assert_arg,utils.function_arg +local normalize_slice = tablex._normalize_slice + +-- metatable for our list and map objects has already been defined.. +local Multimap = utils.stdmt.MultiMap +local List = utils.stdmt.List + +local iter + +class(nil,nil,List) + +-- we want the result to be _covariant_, i.e. t must have type of obj if possible +local function makelist (t,obj) + local klass = List + if obj then + klass = getmetatable(obj) + end + return setmetatable(t,klass) +end + +local function simple_table(t) + return type(t) == 'table' and not getmetatable(t) and #t > 0 +end + +function List._create (src) + if simple_table(src) then return src end +end + +function List:_init (src) + if self == src then return end -- existing table used as self! + if src then + for v in iter(src) do + tinsert(self,v) + end + end +end + +--- Create a new list. Can optionally pass a table; +-- passing another instance of List will cause a copy to be created; +-- this will return a plain table with an appropriate metatable. +-- we pass anything which isn't a simple table to iterate() to work out +-- an appropriate iterator +-- @see List.iterate +-- @param[opt] t An optional list-like table +-- @return a new List +-- @usage ls = List(); ls = List {1,2,3,4} +-- @function List.new + +List.new = List + +--- Make a copy of an existing list. +-- The difference from a plain 'copy constructor' is that this returns +-- the actual List subtype. +function List:clone() + local ls = makelist({},self) + ls:extend(self) + return ls +end + +--- Add an item to the end of the list. +-- @param i An item +-- @return the list +function List:append(i) + tinsert(self,i) + return self +end + +List.push = tinsert + +--- Extend the list by appending all the items in the given list. +-- equivalent to 'a[len(a):] = L'. +-- @tparam List L Another List +-- @return the list +function List:extend(L) + assert_arg(1,L,'table') + for i = 1,#L do tinsert(self,L[i]) end + return self +end + +--- Insert an item at a given position. i is the index of the +-- element before which to insert. +-- @int i index of element before whichh to insert +-- @param x A data item +-- @return the list +function List:insert(i, x) + assert_arg(1,i,'number') + tinsert(self,i,x) + return self +end + +--- Insert an item at the begining of the list. +-- @param x a data item +-- @return the list +function List:put (x) + return self:insert(1,x) +end + +--- Remove an element given its index. +-- (equivalent of Python's del s[i]) +-- @int i the index +-- @return the list +function List:remove (i) + assert_arg(1,i,'number') + tremove(self,i) + return self +end + +--- Remove the first item from the list whose value is given. +-- (This is called 'remove' in Python; renamed to avoid confusion +-- with table.remove) +-- Return nil if there is no such item. +-- @param x A data value +-- @return the list +function List:remove_value(x) + for i=1,#self do + if self[i]==x then tremove(self,i) return self end + end + return self + end + +--- Remove the item at the given position in the list, and return it. +-- If no index is specified, a:pop() returns the last item in the list. +-- The item is also removed from the list. +-- @int[opt] i An index +-- @return the item +function List:pop(i) + if not i then i = #self end + assert_arg(1,i,'number') + return tremove(self,i) +end + +List.get = List.pop + +--- Return the index in the list of the first item whose value is given. +-- Return nil if there is no such item. +-- @function List:index +-- @param x A data value +-- @int[opt=1] idx where to start search +-- @return the index, or nil if not found. + +local tfind = tablex.find +List.index = tfind + +--- Does this list contain the value? +-- @param x A data value +-- @return true or false +function List:contains(x) + return tfind(self,x) and true or false +end + +--- Return the number of times value appears in the list. +-- @param x A data value +-- @return number of times x appears +function List:count(x) + local cnt=0 + for i=1,#self do + if self[i]==x then cnt=cnt+1 end + end + return cnt +end + +--- Sort the items of the list, in place. +-- @func[opt='<'] cmp an optional comparison function +-- @return the list +function List:sort(cmp) + if cmp then cmp = function_arg(1,cmp) end + tsort(self,cmp) + return self +end + +--- Return a sorted copy of this list. +-- @func[opt='<'] cmp an optional comparison function +-- @return a new list +function List:sorted(cmp) + return List(self):sort(cmp) +end + +--- Reverse the elements of the list, in place. +-- @return the list +function List:reverse() + local t = self + local n = #t + for i = 1,n/2 do + t[i],t[n] = t[n],t[i] + n = n - 1 + end + return self +end + +--- Return the minimum and the maximum value of the list. +-- @return minimum value +-- @return maximum value +function List:minmax() + local vmin,vmax = 1e70,-1e70 + for i = 1,#self do + local v = self[i] + if v < vmin then vmin = v end + if v > vmax then vmax = v end + end + return vmin,vmax +end + +--- Emulate list slicing. like 'list[first:last]' in Python. +-- If first or last are negative then they are relative to the end of the list +-- eg. slice(-2) gives last 2 entries in a list, and +-- slice(-4,-2) gives from -4th to -2nd +-- @param first An index +-- @param last An index +-- @return a new List +function List:slice(first,last) + return tsub(self,first,last) +end + +--- Empty the list. +-- @return the list +function List:clear() + for i=1,#self do tremove(self) end + return self +end + +local eps = 1.0e-10 + +--- Emulate Python's range(x) function. +-- Include it in List table for tidiness +-- @int start A number +-- @int[opt] finish A number greater than start; if absent, +-- then start is 1 and finish is start +-- @int[opt=1] incr an increment (may be less than 1) +-- @return a List from start .. finish +-- @usage List.range(0,3) == List{0,1,2,3} +-- @usage List.range(4) = List{1,2,3,4} +-- @usage List.range(5,1,-1) == List{5,4,3,2,1} +function List.range(start,finish,incr) + if not finish then + finish = start + start = 1 + end + if incr then + assert_arg(3,incr,'number') + if math.ceil(incr) ~= incr then finish = finish + eps end + else + incr = 1 + end + assert_arg(1,start,'number') + assert_arg(2,finish,'number') + local t = List() + for i=start,finish,incr do tinsert(t,i) end + return t +end + +--- list:len() is the same as #list. +function List:len() + return #self +end + +-- Extended operations -- + +--- Remove a subrange of elements. +-- equivalent to 'del s[i1:i2]' in Python. +-- @int i1 start of range +-- @int i2 end of range +-- @return the list +function List:chop(i1,i2) + return tremovevalues(self,i1,i2) +end + +--- Insert a sublist into a list +-- equivalent to 's[idx:idx] = list' in Python +-- @int idx index +-- @tparam List list list to insert +-- @return the list +-- @usage l = List{10,20}; l:splice(2,{21,22}); assert(l == List{10,21,22,20}) +function List:splice(idx,list) + assert_arg(1,idx,'number') + idx = idx - 1 + local i = 1 + for v in iter(list) do + tinsert(self,i+idx,v) + i = i + 1 + end + return self +end + +--- General slice assignment s[i1:i2] = seq. +-- @int i1 start index +-- @int i2 end index +-- @tparam List seq a list +-- @return the list +function List:slice_assign(i1,i2,seq) + assert_arg(1,i1,'number') + assert_arg(1,i2,'number') + i1,i2 = normalize_slice(self,i1,i2) + if i2 >= i1 then self:chop(i1,i2) end + self:splice(i1,seq) + return self +end + +--- Concatenation operator. +-- @within metamethods +-- @tparam List L another List +-- @return a new list consisting of the list with the elements of the new list appended +function List:__concat(L) + assert_arg(1,L,'table') + local ls = self:clone() + ls:extend(L) + return ls +end + +--- Equality operator ==. True iff all elements of two lists are equal. +-- @within metamethods +-- @tparam List L another List +-- @return true or false +function List:__eq(L) + if #self ~= #L then return false end + for i = 1,#self do + if self[i] ~= L[i] then return false end + end + return true +end + +--- Join the elements of a list using a delimiter. +-- This method uses tostring on all elements. +-- @string[opt=''] delim a delimiter string, can be empty. +-- @return a string +function List:join (delim) + delim = delim or '' + assert_arg(1,delim,'string') + return concat(array_tostring(self),delim) +end + +--- Join a list of strings.
+-- Uses `table.concat` directly. +-- @function List:concat +-- @string[opt=''] delim a delimiter +-- @return a string +List.concat = concat + +local function tostring_q(val) + local s = tostring(val) + if type(val) == 'string' then + s = '"'..s..'"' + end + return s +end + +--- How our list should be rendered as a string. Uses join(). +-- @within metamethods +-- @see List:join +function List:__tostring() + return '{'..self:join(',',tostring_q)..'}' +end + +--- Call the function on each element of the list. +-- @func fun a function or callable object +-- @param ... optional values to pass to function +function List:foreach (fun,...) + fun = function_arg(1,fun) + for i = 1,#self do + fun(self[i],...) + end +end + +local function lookup_fun (obj,name) + local f = obj[name] + if not f then error(type(obj).." does not have method "..name,3) end + return f +end + +--- Call the named method on each element of the list. +-- @string name the method name +-- @param ... optional values to pass to function +function List:foreachm (name,...) + for i = 1,#self do + local obj = self[i] + local f = lookup_fun(obj,name) + f(obj,...) + end +end + +--- Create a list of all elements which match a function. +-- @func fun a boolean function +-- @param[opt] arg optional argument to be passed as second argument of the predicate +-- @return a new filtered list. +function List:filter (fun,arg) + return makelist(filter(self,fun,arg),self) +end + +--- Split a string using a delimiter. +-- @string s the string +-- @string[opt] delim the delimiter (default spaces) +-- @return a List of strings +-- @see pl.utils.split +function List.split (s,delim) + assert_arg(1,s,'string') + return makelist(split(s,delim)) +end + +--- Apply a function to all elements. +-- Any extra arguments will be passed to the function. +-- @func fun a function of at least one argument +-- @param ... arbitrary extra arguments. +-- @return a new list: {f(x) for x in self} +-- @usage List{'one','two'}:map(string.upper) == {'ONE','TWO'} +-- @see pl.tablex.imap +function List:map (fun,...) + return makelist(imap(fun,self,...),self) +end + +--- Apply a function to all elements, in-place. +-- Any extra arguments are passed to the function. +-- @func fun A function that takes at least one argument +-- @param ... arbitrary extra arguments. +-- @return the list. +function List:transform (fun,...) + transform(fun,self,...) + return self +end + +--- Apply a function to elements of two lists. +-- Any extra arguments will be passed to the function +-- @func fun a function of at least two arguments +-- @tparam List ls another list +-- @param ... arbitrary extra arguments. +-- @return a new list: {f(x,y) for x in self, for x in arg1} +-- @see pl.tablex.imap2 +function List:map2 (fun,ls,...) + return makelist(imap2(fun,self,ls,...),self) +end + +--- apply a named method to all elements. +-- Any extra arguments will be passed to the method. +-- @string name name of method +-- @param ... extra arguments +-- @return a new list of the results +-- @see pl.seq.mapmethod +function List:mapm (name,...) + local res = {} + for i = 1,#self do + local val = self[i] + local fn = lookup_fun(val,name) + res[i] = fn(val,...) + end + return makelist(res,self) +end + +local function composite_call (method,f) + return function(self,...) + return self[method](self,f,...) + end +end + +function List.default_map_with(T) + return function(self,name) + local m + if T then + local f = lookup_fun(T,name) + m = composite_call('map',f) + else + m = composite_call('mapn',name) + end + getmetatable(self)[name] = m -- and cache.. + return m + end +end + +List.default_map = List.default_map_with + +--- 'reduce' a list using a binary function. +-- @func fun a function of two arguments +-- @return result of the function +-- @see pl.tablex.reduce +function List:reduce (fun) + return reduce(fun,self) +end + +--- Partition a list using a classifier function. +-- The function may return nil, but this will be converted to the string key ''. +-- @func fun a function of at least one argument +-- @param ... will also be passed to the function +-- @treturn MultiMap a table where the keys are the returned values, and the values are Lists +-- of values where the function returned that key. +-- @see pl.MultiMap +function List:partition (fun,...) + fun = function_arg(1,fun) + local res = {} + for i = 1,#self do + local val = self[i] + local klass = fun(val,...) + if klass == nil then klass = '' end + if not res[klass] then res[klass] = List() end + res[klass]:append(val) + end + return setmetatable(res,Multimap) +end + +--- return an iterator over all values. +function List:iter () + return iter(self) +end + +--- Create an iterator over a seqence. +-- This captures the Python concept of 'sequence'. +-- For tables, iterates over all values with integer indices. +-- @param seq a sequence; a string (over characters), a table, a file object (over lines) or an iterator function +-- @usage for x in iterate {1,10,22,55} do io.write(x,',') end ==> 1,10,22,55 +-- @usage for ch in iterate 'help' do do io.write(ch,' ') end ==> h e l p +function List.iterate(seq) + if type(seq) == 'string' then + local idx = 0 + local n = #seq + local sub = string.sub + return function () + idx = idx + 1 + if idx > n then return nil + else + return sub(seq,idx,idx) + end + end + elseif type(seq) == 'table' then + local idx = 0 + local n = #seq + return function() + idx = idx + 1 + if idx > n then return nil + else + return seq[idx] + end + end + elseif type(seq) == 'function' then + return seq + elseif type(seq) == 'userdata' and io.type(seq) == 'file' then + return seq:lines() + end +end +iter = List.iterate + +return List + diff --git a/framework/lualib/thirdparty/pl/Map.lua b/framework/lualib/thirdparty/pl/Map.lua new file mode 100755 index 0000000..572b22a --- /dev/null +++ b/framework/lualib/thirdparty/pl/Map.lua @@ -0,0 +1,120 @@ +--- A Map class. +-- +-- > Map = require 'pl.Map' +-- > m = Map{one=1,two=2} +-- > m:update {three=3,four=4,two=20} +-- > = m == M{one=1,two=20,three=3,four=4} +-- true +-- +-- Dependencies: `pl.utils`, `pl.class`, `pl.tablex`, `pl.pretty` +-- @classmod pl.Map + +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local stdmt = utils.stdmt +local deepcompare = tablex.deepcompare + +local pretty_write = require 'pl.pretty' . write +local Map = stdmt.Map +local Set = stdmt.Set + +local class = require 'pl.class' + +-- the Map class --------------------- +class(nil,nil,Map) + +function Map:_init (t) + local mt = getmetatable(t) + if mt == Set or mt == Map then + self:update(t) + else + return t -- otherwise assumed to be a map-like table + end +end + + +local function makelist(t) + return setmetatable(t, require('pl.List')) +end + +--- list of keys. +Map.keys = tablex.keys + +--- list of values. +Map.values = tablex.values + +--- return an iterator over all key-value pairs. +function Map:iter () + return pairs(self) +end + +--- return a List of all key-value pairs, sorted by the keys. +function Map:items() + local ls = makelist(tablex.pairmap (function (k,v) return makelist {k,v} end, self)) + ls:sort(function(t1,t2) return t1[1] < t2[1] end) + return ls +end + +--- set a value in the map if it doesn't exist yet. +-- @param key the key +-- @param default value to set +-- @return the value stored in the map (existing value, or the new value) +function Map:setdefault(key, default) + local val = self[key] + if val ~= nil then + return val + end + self:set(key,default) + return default +end + +--- size of map. +-- note: this is a relatively expensive operation! +-- @class function +-- @name Map:len +Map.len = tablex.size + +--- put a value into the map. +-- This will remove the key if the value is `nil` +-- @param key the key +-- @param val the value +function Map:set (key,val) + self[key] = val +end + +--- get a value from the map. +-- @param key the key +-- @return the value, or nil if not found. +function Map:get (key) + return rawget(self,key) +end + +local index_by = tablex.index_by + +--- get a list of values indexed by a list of keys. +-- @param keys a list-like table of keys +-- @return a new list +function Map:getvalues (keys) + return makelist(index_by(self,keys)) +end + +--- update the map using key/value pairs from another table. +-- @tab table +-- @function Map:update +Map.update = tablex.update + +--- equality between maps. +-- @within metamethods +-- @tparam Map m another map. +function Map:__eq (m) + -- note we explicitly ask deepcompare _not_ to use __eq! + return deepcompare(self,m,true) +end + +--- string representation of a map. +-- @within metamethods +function Map:__tostring () + return pretty_write(self,'') +end + +return Map diff --git a/framework/lualib/thirdparty/pl/MultiMap.lua b/framework/lualib/thirdparty/pl/MultiMap.lua new file mode 100755 index 0000000..0abe84d --- /dev/null +++ b/framework/lualib/thirdparty/pl/MultiMap.lua @@ -0,0 +1,54 @@ +--- MultiMap, a Map which has multiple values per key. +-- +-- Dependencies: `pl.utils`, `pl.class`, `pl.List`, `pl.Map` +-- @classmod pl.MultiMap + +local utils = require 'pl.utils' +local class = require 'pl.class' +local List = require 'pl.List' +local Map = require 'pl.Map' + +-- MultiMap is a standard MT +local MultiMap = utils.stdmt.MultiMap + +class(Map,nil,MultiMap) +MultiMap._name = 'MultiMap' + +function MultiMap:_init (t) + if not t then return end + self:update(t) +end + +--- update a MultiMap using a table. +-- @param t either a Multimap or a map-like table. +-- @return the map +function MultiMap:update (t) + utils.assert_arg(1,t,'table') + if Map:class_of(t) then + for k,v in pairs(t) do + self[k] = List() + self[k]:append(v) + end + else + for k,v in pairs(t) do + self[k] = List(v) + end + end +end + +--- add a new value to a key. Setting a nil value removes the key. +-- @param key the key +-- @param val the value +-- @return the map +function MultiMap:set (key,val) + if val == nil then + self[key] = nil + else + if not self[key] then + self[key] = List() + end + self[key]:append(val) + end +end + +return MultiMap diff --git a/framework/lualib/thirdparty/pl/OrderedMap.lua b/framework/lualib/thirdparty/pl/OrderedMap.lua new file mode 100755 index 0000000..379c44f --- /dev/null +++ b/framework/lualib/thirdparty/pl/OrderedMap.lua @@ -0,0 +1,167 @@ +--- OrderedMap, a map which preserves ordering. +-- +-- Derived from `pl.Map`. +-- +-- Dependencies: `pl.utils`, `pl.tablex`, `pl.class`, `pl.List`, `pl.Map` +-- @classmod pl.OrderedMap + +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local List = require 'pl.List' +local index_by,tsort,concat = tablex.index_by,table.sort,table.concat + +local class = require 'pl.class' +local Map = require 'pl.Map' + +local OrderedMap = class(Map) +OrderedMap._name = 'OrderedMap' + +local rawset = rawset + +--- construct an OrderedMap. +-- Will throw an error if the argument is bad. +-- @param t optional initialization table, same as for @{OrderedMap:update} +function OrderedMap:_init (t) + rawset(self,'_keys',List()) + if t then + local map,err = self:update(t) + if not map then error(err,2) end + end +end + +local assert_arg,raise = utils.assert_arg,utils.raise + +--- update an OrderedMap using a table. +-- If the table is itself an OrderedMap, then its entries will be appended. +-- if it s a table of the form `{{key1=val1},{key2=val2},...}` these will be appended. +-- +-- Otherwise, it is assumed to be a map-like table, and order of extra entries is arbitrary. +-- @tab t a table. +-- @return the map, or nil in case of error +-- @return the error message +function OrderedMap:update (t) + assert_arg(1,t,'table') + if OrderedMap:class_of(t) then + for k,v in t:iter() do + self:set(k,v) + end + elseif #t > 0 then -- an array must contain {key=val} tables + if type(t[1]) == 'table' then + for _,pair in ipairs(t) do + local key,value = next(pair) + if not key then return raise 'empty pair initialization table' end + self:set(key,value) + end + else + return raise 'cannot use an array to initialize an OrderedMap' + end + else + for k,v in pairs(t) do + self:set(k,v) + end + end + return self +end + +--- set the key's value. This key will be appended at the end of the map. +-- +-- If the value is nil, then the key is removed. +-- @param key the key +-- @param val the value +-- @return the map +function OrderedMap:set (key,val) + if rawget(self, key) == nil and val ~= nil then -- new key + self._keys:append(key) -- we keep in order + rawset(self,key,val) -- don't want to provoke __newindex! + else -- existing key-value pair + if val == nil then + self._keys:remove_value(key) + rawset(self,key,nil) + else + self[key] = val + end + end + return self +end + +OrderedMap.__newindex = OrderedMap.set + +--- insert a key/value pair before a given position. +-- Note: if the map already contains the key, then this effectively +-- moves the item to the new position by first removing at the old position. +-- Has no effect if the key does not exist and val is nil +-- @int pos a position starting at 1 +-- @param key the key +-- @param val the value; if nil use the old value +function OrderedMap:insert (pos,key,val) + local oldval = self[key] + val = val or oldval + if oldval then + self._keys:remove_value(key) + end + if val then + self._keys:insert(pos,key) + rawset(self,key,val) + end + return self +end + +--- return the keys in order. +-- (Not a copy!) +-- @return List +function OrderedMap:keys () + return self._keys +end + +--- return the values in order. +-- this is relatively expensive. +-- @return List +function OrderedMap:values () + return List(index_by(self,self._keys)) +end + +--- sort the keys. +-- @func cmp a comparison function as for @{table.sort} +-- @return the map +function OrderedMap:sort (cmp) + tsort(self._keys,cmp) + return self +end + +--- iterate over key-value pairs in order. +function OrderedMap:iter () + local i = 0 + local keys = self._keys + local idx + return function() + i = i + 1 + if i > #keys then return nil end + idx = keys[i] + return idx,self[idx] + end +end + +--- iterate over an ordered map (5.2). +-- @within metamethods +-- @function OrderedMap:__pairs +OrderedMap.__pairs = OrderedMap.iter + +--- string representation of an ordered map. +-- @within metamethods +function OrderedMap:__tostring () + local res = {} + for i,v in ipairs(self._keys) do + local val = self[v] + local vs = tostring(val) + if type(val) ~= 'number' then + vs = '"'..vs..'"' + end + res[i] = tostring(v)..'='..vs + end + return '{'..concat(res,',')..'}' +end + +return OrderedMap + + + diff --git a/framework/lualib/thirdparty/pl/Set.lua b/framework/lualib/thirdparty/pl/Set.lua new file mode 100755 index 0000000..ce428f0 --- /dev/null +++ b/framework/lualib/thirdparty/pl/Set.lua @@ -0,0 +1,222 @@ +--- A Set class. +-- +-- > Set = require 'pl.Set' +-- > = Set{'one','two'} == Set{'two','one'} +-- true +-- > fruit = Set{'apple','banana','orange'} +-- > = fruit['banana'] +-- true +-- > = fruit['hazelnut'] +-- nil +-- > colours = Set{'red','orange','green','blue'} +-- > = fruit,colours +-- [apple,orange,banana] [blue,green,orange,red] +-- > = fruit+colours +-- [blue,green,apple,red,orange,banana] +-- [orange] +-- > more_fruits = fruit + 'apricot' +-- > = fruit*colours +-- > = more_fruits, fruit +-- [banana,apricot,apple,orange] [banana,apple,orange] +-- +-- Dependencies: `pl.utils`, `pl.tablex`, `pl.class`, `pl.Map`, (`pl.List` if __tostring is used) +-- @classmod pl.Set + +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local array_tostring, concat = utils.array_tostring, table.concat +local merge,difference = tablex.merge,tablex.difference +local Map = require 'pl.Map' +local class = require 'pl.class' +local stdmt = utils.stdmt +local Set = stdmt.Set + +-- the Set class -------------------- +class(Map,nil,Set) + +-- note: Set has _no_ methods! +Set.__index = nil + +local function makeset (t) + return setmetatable(t,Set) +end + +--- create a set.
+-- @param t may be a Set, Map or list-like table. +-- @class function +-- @name Set +function Set:_init (t) + t = t or {} + local mt = getmetatable(t) + if mt == Set or mt == Map then + for k in pairs(t) do self[k] = true end + else + for _,v in ipairs(t) do self[v] = true end + end +end + +--- string representation of a set. +-- @within metamethods +function Set:__tostring () + return '['..concat(array_tostring(Set.values(self)),',')..']' +end + +--- get a list of the values in a set. +-- @param self a Set +-- @function Set.values +-- @return a list +Set.values = Map.keys + +--- map a function over the values of a set. +-- @param self a Set +-- @param fn a function +-- @param ... extra arguments to pass to the function. +-- @return a new set +function Set.map (self,fn,...) + fn = utils.function_arg(1,fn) + local res = {} + for k in pairs(self) do + res[fn(k,...)] = true + end + return makeset(res) +end + +--- union of two sets (also +). +-- @param self a Set +-- @param set another set +-- @return a new set +function Set.union (self,set) + return merge(self,set,true) +end + +--- modifies '+' operator to allow addition of non-Set elements +--- Preserves +/- semantics - does not modify first argument. +local function setadd(self,other) + local mt = getmetatable(other) + if mt == Set or mt == Map then + return Set.union(self,other) + else + local new = Set(self) + new[other] = true + return new + end +end + +--- union of sets. +-- @within metamethods +-- @function Set.__add + +Set.__add = setadd + +--- intersection of two sets (also *). +-- @param self a Set +-- @param set another set +-- @return a new set +-- @usage +-- > s = Set{10,20,30} +-- > t = Set{20,30,40} +-- > = t +-- [20,30,40] +-- > = Set.intersection(s,t) +-- [30,20] +-- > = s*t +-- [30,20] + +function Set.intersection (self,set) + return merge(self,set,false) +end + +--- intersection of sets. +-- @within metamethods +-- @function Set.__mul +Set.__mul = Set.intersection + +--- new set with elements in the set that are not in the other (also -). +-- @param self a Set +-- @param set another set +-- @return a new set +function Set.difference (self,set) + return difference(self,set,false) +end + +--- modifies "-" operator to remove non-Set values from set. +--- Preserves +/- semantics - does not modify first argument. +local function setminus (self,other) + local mt = getmetatable(other) + if mt == Set or mt == Map then + return Set.difference(self,other) + else + local new = Set(self) + new[other] = nil + return new + end +end + +--- difference of sets. +-- @within metamethods +-- @function Set.__sub +Set.__sub = setminus + +-- a new set with elements in _either_ the set _or_ other but not both (also ^). +-- @param self a Set +-- @param set another set +-- @return a new set +function Set.symmetric_difference (self,set) + return difference(self,set,true) +end + +--- symmetric difference of sets. +-- @within metamethods +-- @function Set.__pow +Set.__pow = Set.symmetric_difference + +--- is the first set a subset of the second (also <)?. +-- @param self a Set +-- @param set another set +-- @return true or false +function Set.issubset (self,set) + for k in pairs(self) do + if not set[k] then return false end + end + return true +end + +--- first set subset of second? +-- @within metamethods +-- @function Set.__lt +Set.__lt = Set.issubset + +--- is the set empty?. +-- @param self a Set +-- @return true or false +function Set.isempty (self) + return next(self) == nil +end + +--- are the sets disjoint? (no elements in common). +-- Uses naive definition, i.e. that intersection is empty +-- @param s1 a Set +-- @param s2 another set +-- @return true or false +function Set.isdisjoint (s1,s2) + return Set.isempty(Set.intersection(s1,s2)) +end + +--- size of this set (also # for 5.2). +-- @param s a Set +-- @return size +-- @function Set.len +Set.len = tablex.size + +--- cardinality of set (5.2). +-- @within metamethods +-- @function Set.__len +Set.__len = Set.len + +--- equality between sets. +-- @within metamethods +function Set.__eq (s1,s2) + return Set.issubset(s1,s2) and Set.issubset(s2,s1) +end + +return Set diff --git a/framework/lualib/thirdparty/pl/app.lua b/framework/lualib/thirdparty/pl/app.lua new file mode 100755 index 0000000..a06305d --- /dev/null +++ b/framework/lualib/thirdparty/pl/app.lua @@ -0,0 +1,297 @@ +--- Application support functions. +-- See @{01-introduction.md.Application_Support|the Guide} +-- +-- Dependencies: `pl.utils`, `pl.path` +-- @module pl.app + +local io,package,require = _G.io, _G.package, _G.require +local utils = require 'pl.utils' +local path = require 'pl.path' + +local app = {} + +--- return the name of the current script running. +-- The name will be the name as passed on the command line +-- @return string filename +function app.script_name() + if _G.arg and _G.arg[0] then + return _G.arg[0] + end + return utils.raise("No script name found") +end + +--- prefixes the current script's path to the Lua module path. +-- Applies to both the source and the binary module paths. It makes it easy for +-- the main file of a multi-file program to access its modules in the same directory. +-- `base` allows these modules to be put in a specified subdirectory, to allow for +-- cleaner deployment and resolve potential conflicts between a script name and its +-- library directory. +-- +-- Note: the path is prefixed, so it is searched first when requiring modules. +-- @string base optional base directory (absolute, or relative path). +-- @treturn string the current script's path with a trailing slash +function app.require_here (base) + local p = path.dirname(app.script_name()) + if not path.isabs(p) then + p = path.join(path.currentdir(),p) + end + if p:sub(-1,-1) ~= path.sep then + p = p..path.sep + end + if base then + if path.is_windows then + base = base:gsub('/','\\') + end + if path.isabs(base) then + p = base .. path.sep + else + p = p..base..path.sep + end + end + local so_ext = path.is_windows and 'dll' or 'so' + local lsep = package.path:find '^;' and '' or ';' + local csep = package.cpath:find '^;' and '' or ';' + package.path = ('%s?.lua;%s?%sinit.lua%s%s'):format(p,p,path.sep,lsep,package.path) + package.cpath = ('%s?.%s%s%s'):format(p,so_ext,csep,package.cpath) + return p +end + +--- return a suitable path for files private to this application. +-- These will look like '~/.SNAME/file', with '~' as with expanduser and +-- SNAME is the name of the script without .lua extension. +-- If the directory does not exist, it will be created. +-- @string file a filename (w/out path) +-- @return a full pathname, or nil +-- @return cannot create directory error +-- @usage +-- -- when run from a script called 'testapp' (on Windows): +-- local app = require 'pl.app' +-- print(app.appfile 'test.txt') +-- -- C:\Documents and Settings\steve\.testapp\test.txt +function app.appfile(file) + local sfullname, err = app.script_name() + if not sfullname then return utils.raise(err) end + local sname = path.basename(sfullname) + local name = path.splitext(sname) + local dir = path.join(path.expanduser('~'),'.'..name) + if not path.isdir(dir) then + local ret = path.mkdir(dir) + if not ret then return utils.raise('cannot create '..dir) end + end + return path.join(dir,file) +end + +--- return string indicating operating system. +-- @return 'Windows','OSX' or whatever uname returns (e.g. 'Linux') +function app.platform() + if path.is_windows then + return 'Windows' + else + local f = io.popen('uname') + local res = f:read() + if res == 'Darwin' then res = 'OSX' end + f:close() + return res + end +end + +--- return the full command-line used to invoke this script. +-- It will not include the scriptname itself, see `app.script_name`. +-- @return command-line +-- @return name of Lua program used +-- @usage +-- -- execute: lua -lluacov -e 'print(_VERSION)' myscript.lua +-- +-- -- myscript.lua +-- print(require("pl.app").lua()) --> "lua -lluacov -e 'print(_VERSION)'", "lua" +function app.lua() + local args = _G.arg + if not args then + return utils.raise "not in a main program" + end + + local cmd = {} + local i = -1 + while true do + table.insert(cmd, 1, args[i]) + if not args[i-1] then + return utils.quote_arg(cmd), args[i] + end + i = i - 1 + end +end + +--- parse command-line arguments into flags and parameters. +-- Understands GNU-style command-line flags; short (`-f`) and long (`--flag`). +-- +-- These may be given a value with either '=' or ':' (`-k:2`,`--alpha=3.2`,`-n2`), +-- a number value can be given without a space. If the flag is marked +-- as having a value, then a space-separated value is also accepted (`-i hello`), +-- see the `flags_with_values` argument). +-- +-- Multiple short args can be combined like so: ( `-abcd`). +-- +-- When specifying the `flags_valid` parameter, its contents can also contain +-- aliasses, to convert short/long flags to the same output name. See the +-- example below. +-- +-- Note: if a flag is repeated, the last value wins. +-- @tparam {string} args an array of strings (default is the global `arg`) +-- @tab flags_with_values any flags that take values, either list or hash +-- table e.g. `{ out=true }` or `{ "out" }`. +-- @tab flags_valid (optional) flags that are valid, either list or hashtable. +-- If not given, everything +-- will be accepted(everything in `flags_with_values` will automatically be allowed) +-- @return a table of flags (flag=value pairs) +-- @return an array of parameters +-- @raise if args is nil, then the global `args` must be available! +-- @usage +-- -- Simple form: +-- local flags, params = app.parse_args(nil, +-- { "hello", "world" }, -- list of flags taking values +-- { "l", "a", "b"}) -- list of allowed flags (value ones will be added) +-- +-- -- More complex example using aliasses: +-- local valid = { +-- long = "l", -- if 'l' is specified, it is reported as 'long' +-- new = { "n", "old" }, -- here both 'n' and 'old' will go into 'new' +-- } +-- local values = { +-- "value", -- will automatically be added to the allowed set of flags +-- "new", -- will mark 'n' and 'old' as requiring a value as well +-- } +-- local flags, params = app.parse_args(nil, values, valid) +-- +-- -- command: myapp.lua -l --old:hello --value world param1 param2 +-- -- will yield: +-- flags = { +-- long = true, -- input from 'l' +-- new = "hello", -- input from 'old' +-- value = "world", -- allowed because it was in 'values', note: space separated! +-- } +-- params = { +-- [1] = "param1" +-- [2] = "param2" +-- } +function app.parse_args (args,flags_with_values, flags_valid) + if not args then + args = _G.arg + if not args then utils.raise "Not in a main program: 'arg' not found" end + end + + local with_values = {} + for k,v in pairs(flags_with_values or {}) do + if type(k) == "number" then + k = v + end + with_values[k] = true + end + + local valid + if not flags_valid then + -- if no allowed flags provided, we create a table that always returns + -- the keyname, no matter what you look up + valid = setmetatable({},{ __index = function(_, key) return key end }) + else + valid = {} + for k,aliasses in pairs(flags_valid) do + if type(k) == "number" then -- array/list entry + k = aliasses + end + if type(aliasses) == "string" then -- single alias + aliasses = { aliasses } + end + if type(aliasses) == "table" then -- list of aliasses + -- it's the alternate name, so add the proper mappings + for i, alias in ipairs(aliasses) do + valid[alias] = k + end + end + valid[k] = k + end + do + local new_with_values = {} -- needed to prevent "invalid key to 'next'" error + for k,v in pairs(with_values) do + if not valid[k] then + valid[k] = k -- add the with_value entry as a valid one + new_with_values[k] = true + else + new_with_values[valid[k]] = true --set, but by its alias + end + end + with_values = new_with_values + end + end + + -- now check that all flags with values are reported as such under all + -- of their aliasses + for k, main_alias in pairs(valid) do + if with_values[main_alias] then + with_values[k] = true + end + end + + local _args = {} + local flags = {} + local i = 1 + while i <= #args do + local a = args[i] + local v = a:match('^-(.+)') + local is_long + if not v then + -- we have a parameter + _args[#_args+1] = a + else + -- it's a flag + if v:find '^-' then + is_long = true + v = v:sub(2) + end + if with_values[v] then + if i == #args or args[i+1]:find '^-' then + return utils.raise ("no value for '"..v.."'") + end + flags[valid[v]] = args[i+1] + i = i + 1 + else + -- a value can also be indicated with = or : + local var,val = utils.splitv (v,'[=:]', false, 2) + var = var or v + val = val or true + if not is_long then + if #var > 1 then + if var:find '.%d+' then -- short flag, number value + val = var:sub(2) + var = var:sub(1,1) + else -- multiple short flags + for i = 1,#var do + local f = var:sub(i,i) + if not valid[f] then + return utils.raise("unknown flag '"..f.."'") + else + f = valid[f] + end + flags[f] = true + end + val = nil -- prevents use of var as a flag below + end + else -- single short flag (can have value, defaults to true) + val = val or true + end + end + if val then + if not valid[var] then + return utils.raise("unknown flag '"..var.."'") + else + var = valid[var] + end + flags[var] = val + end + end + end + i = i + 1 + end + return flags,_args +end + +return app diff --git a/framework/lualib/thirdparty/pl/array2d.lua b/framework/lualib/thirdparty/pl/array2d.lua new file mode 100755 index 0000000..40b4eca --- /dev/null +++ b/framework/lualib/thirdparty/pl/array2d.lua @@ -0,0 +1,493 @@ +--- Operations on two-dimensional arrays. +-- See @{02-arrays.md.Operations_on_two_dimensional_tables|The Guide} +-- +-- Dependencies: `pl.utils`, `pl.tablex`, `pl.types` +-- @module pl.array2d + +local tonumber,assert,tostring,io,ipairs,string,table = + _G.tonumber,_G.assert,_G.tostring,_G.io,_G.ipairs,_G.string,_G.table +local setmetatable,getmetatable = setmetatable,getmetatable + +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local types = require 'pl.types' +local imap,tmap,reduce,keys,tmap2,tset,index_by = tablex.imap,tablex.map,tablex.reduce,tablex.keys,tablex.map2,tablex.set,tablex.index_by +local remove = table.remove +local splitv,fprintf,assert_arg = utils.splitv,utils.fprintf,utils.assert_arg +local byte = string.byte +local stdout = io.stdout +local min = math.min + + +local array2d = {} + +local function obj (int,out) + local mt = getmetatable(int) + if mt then + setmetatable(out,mt) + end + return out +end + +local function makelist (res) + return setmetatable(res, require('pl.List')) +end + + +local function index (t,k) + return t[k] +end + +--- return the row and column size. +-- @array2d t a 2d array +-- @treturn int number of rows +-- @treturn int number of cols +function array2d.size (t) + assert_arg(1,t,'table') + return #t,#t[1] +end + +--- extract a column from the 2D array. +-- @array2d a 2d array +-- @param key an index or key +-- @return 1d array +function array2d.column (a,key) + assert_arg(1,a,'table') + return makelist(imap(index,a,key)) +end +local column = array2d.column + +--- map a function over a 2D array +-- @func f a function of at least one argument +-- @array2d a 2d array +-- @param arg an optional extra argument to be passed to the function. +-- @return 2d array +function array2d.map (f,a,arg) + assert_arg(1,a,'table') + f = utils.function_arg(1,f) + return obj(a,imap(function(row) return imap(f,row,arg) end, a)) +end + +--- reduce the rows using a function. +-- @func f a binary function +-- @array2d a 2d array +-- @return 1d array +-- @see pl.tablex.reduce +function array2d.reduce_rows (f,a) + assert_arg(1,a,'table') + return tmap(function(row) return reduce(f,row) end, a) +end + +--- reduce the columns using a function. +-- @func f a binary function +-- @array2d a 2d array +-- @return 1d array +-- @see pl.tablex.reduce +function array2d.reduce_cols (f,a) + assert_arg(1,a,'table') + return tmap(function(c) return reduce(f,column(a,c)) end, keys(a[1])) +end + +--- reduce a 2D array into a scalar, using two operations. +-- @func opc operation to reduce the final result +-- @func opr operation to reduce the rows +-- @param a 2D array +function array2d.reduce2 (opc,opr,a) + assert_arg(3,a,'table') + local tmp = array2d.reduce_rows(opr,a) + return reduce(opc,tmp) +end + +--- map a function over two arrays. +-- They can be both or either 2D arrays +-- @func f function of at least two arguments +-- @int ad order of first array (1 or 2) +-- @int bd order of second array (1 or 2) +-- @tab a 1d or 2d array +-- @tab b 1d or 2d array +-- @param arg optional extra argument to pass to function +-- @return 2D array, unless both arrays are 1D +function array2d.map2 (f,ad,bd,a,b,arg) + assert_arg(1,a,'table') + assert_arg(2,b,'table') + f = utils.function_arg(1,f) + if ad == 1 and bd == 2 then + return imap(function(row) + return tmap2(f,a,row,arg) + end, b) + elseif ad == 2 and bd == 1 then + return imap(function(row) + return tmap2(f,row,b,arg) + end, a) + elseif ad == 1 and bd == 1 then + return tmap2(f,a,b) + elseif ad == 2 and bd == 2 then + return tmap2(function(rowa,rowb) + return tmap2(f,rowa,rowb,arg) + end, a,b) + end +end + +--- cartesian product of two 1d arrays. +-- @func f a function of 2 arguments +-- @array t1 a 1d table +-- @array t2 a 1d table +-- @return 2d table +-- @usage product('..',{1,2},{'a','b'}) == {{'1a','2a'},{'1b','2b'}} +function array2d.product (f,t1,t2) + f = utils.function_arg(1,f) + assert_arg(2,t1,'table') + assert_arg(3,t2,'table') + local res, map = {}, tablex.map + for i,v in ipairs(t2) do + res[i] = map(f,t1,v) + end + return res +end + +--- flatten a 2D array. +-- (this goes over columns first.) +-- @array2d t 2d table +-- @return a 1d table +-- @usage flatten {{1,2},{3,4},{5,6}} == {1,2,3,4,5,6} +function array2d.flatten (t) + local res = {} + local k = 1 + for _,a in ipairs(t) do -- for all rows + for i = 1,#a do + res[k] = a[i] + k = k + 1 + end + end + return makelist(res) +end + +--- reshape a 2D array. +-- @array2d t 2d array +-- @int nrows new number of rows +-- @bool co column-order (Fortran-style) (default false) +-- @return a new 2d array +function array2d.reshape (t,nrows,co) + local nr,nc = array2d.size(t) + local ncols = nr*nc / nrows + local res = {} + local ir,ic = 1,1 + for i = 1,nrows do + local row = {} + for j = 1,ncols do + row[j] = t[ir][ic] + if not co then + ic = ic + 1 + if ic > nc then + ir = ir + 1 + ic = 1 + end + else + ir = ir + 1 + if ir > nr then + ic = ic + 1 + ir = 1 + end + end + end + res[i] = row + end + return obj(t,res) +end + +--- swap two rows of an array. +-- @array2d t a 2d array +-- @int i1 a row index +-- @int i2 a row index +function array2d.swap_rows (t,i1,i2) + assert_arg(1,t,'table') + t[i1],t[i2] = t[i2],t[i1] +end + +--- swap two columns of an array. +-- @array2d t a 2d array +-- @int j1 a column index +-- @int j2 a column index +function array2d.swap_cols (t,j1,j2) + assert_arg(1,t,'table') + for i = 1,#t do + local row = t[i] + row[j1],row[j2] = row[j2],row[j1] + end +end + +--- extract the specified rows. +-- @array2d t 2d array +-- @tparam {int} ridx a table of row indices +function array2d.extract_rows (t,ridx) + return obj(t,index_by(t,ridx)) +end + +--- extract the specified columns. +-- @array2d t 2d array +-- @tparam {int} cidx a table of column indices +function array2d.extract_cols (t,cidx) + assert_arg(1,t,'table') + local res = {} + for i = 1,#t do + res[i] = index_by(t[i],cidx) + end + return obj(t,res) +end + +--- remove a row from an array. +-- @function array2d.remove_row +-- @array2d t a 2d array +-- @int i a row index +array2d.remove_row = remove + +--- remove a column from an array. +-- @array2d t a 2d array +-- @int j a column index +function array2d.remove_col (t,j) + assert_arg(1,t,'table') + for i = 1,#t do + remove(t[i],j) + end +end + +local function _parse (s) + local c,r + if s:sub(1,1) == 'R' then + r,c = s:match 'R(%d+)C(%d+)' + r,c = tonumber(r),tonumber(c) + else + c,r = s:match '(.)(.)' + c = byte(c) - byte 'A' + 1 + r = tonumber(r) + end + assert(c ~= nil and r ~= nil,'bad cell specifier: '..s) + return r,c +end + +--- parse a spreadsheet range. +-- The range can be specified either as 'A1:B2' or 'R1C1:R2C2'; +-- a special case is a single element (e.g 'A1' or 'R1C1') +-- @string s a range. +-- @treturn int start col +-- @treturn int start row +-- @treturn int end col +-- @treturn int end row +function array2d.parse_range (s) + if s:find ':' then + local start,finish = splitv(s,':') + local i1,j1 = _parse(start) + local i2,j2 = _parse(finish) + return i1,j1,i2,j2 + else -- single value + local i,j = _parse(s) + return i,j + end +end + +--- get a slice of a 2D array using spreadsheet range notation. @see parse_range +-- @array2d t a 2D array +-- @string rstr range expression +-- @return a slice +-- @see array2d.parse_range +-- @see array2d.slice +function array2d.range (t,rstr) + assert_arg(1,t,'table') + local i1,j1,i2,j2 = array2d.parse_range(rstr) + if i2 then + return array2d.slice(t,i1,j1,i2,j2) + else -- single value + return t[i1][j1] + end +end + +local function default_range (t,i1,j1,i2,j2) + local nr, nc = array2d.size(t) + i1,j1 = i1 or 1, j1 or 1 + i2,j2 = i2 or nr, j2 or nc + if i2 < 0 then i2 = nr + i2 + 1 end + if j2 < 0 then j2 = nc + j2 + 1 end + return i1,j1,i2,j2 +end + +--- get a slice of a 2D array. Note that if the specified range has +-- a 1D result, the rank of the result will be 1. +-- @array2d t a 2D array +-- @int i1 start row (default 1) +-- @int j1 start col (default 1) +-- @int i2 end row (default N) +-- @int j2 end col (default M) +-- @return an array, 2D in general but 1D in special cases. +function array2d.slice (t,i1,j1,i2,j2) + assert_arg(1,t,'table') + i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2) + local res = {} + for i = i1,i2 do + local val + local row = t[i] + if j1 == j2 then + val = row[j1] + else + val = {} + for j = j1,j2 do + val[#val+1] = row[j] + end + end + res[#res+1] = val + end + if i1 == i2 then res = res[1] end + return obj(t,res) +end + +--- set a specified range of an array to a value. +-- @array2d t a 2D array +-- @param value the value (may be a function) +-- @int i1 start row (default 1) +-- @int j1 start col (default 1) +-- @int i2 end row (default N) +-- @int j2 end col (default M) +-- @see tablex.set +function array2d.set (t,value,i1,j1,i2,j2) + i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2) + for i = i1,i2 do + tset(t[i],value,j1,j2) + end +end + +--- write a 2D array to a file. +-- @array2d t a 2D array +-- @param f a file object (default stdout) +-- @string fmt a format string (default is just to use tostring) +-- @int i1 start row (default 1) +-- @int j1 start col (default 1) +-- @int i2 end row (default N) +-- @int j2 end col (default M) +function array2d.write (t,f,fmt,i1,j1,i2,j2) + assert_arg(1,t,'table') + f = f or stdout + local rowop + if fmt then + rowop = function(row,j) fprintf(f,fmt,row[j]) end + else + rowop = function(row,j) f:write(tostring(row[j]),' ') end + end + local function newline() + f:write '\n' + end + array2d.forall(t,rowop,newline,i1,j1,i2,j2) +end + +--- perform an operation for all values in a 2D array. +-- @array2d t 2D array +-- @func row_op function to call on each value +-- @func end_row_op function to call at end of each row +-- @int i1 start row (default 1) +-- @int j1 start col (default 1) +-- @int i2 end row (default N) +-- @int j2 end col (default M) +function array2d.forall (t,row_op,end_row_op,i1,j1,i2,j2) + assert_arg(1,t,'table') + i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2) + for i = i1,i2 do + local row = t[i] + for j = j1,j2 do + row_op(row,j) + end + if end_row_op then end_row_op(i) end + end +end + +---- move a block from the destination to the source. +-- @array2d dest a 2D array +-- @int di start row in dest +-- @int dj start col in dest +-- @array2d src a 2D array +-- @int i1 start row (default 1) +-- @int j1 start col (default 1) +-- @int i2 end row (default N) +-- @int j2 end col (default M) +function array2d.move (dest,di,dj,src,i1,j1,i2,j2) + assert_arg(1,dest,'table') + assert_arg(4,src,'table') + i1,j1,i2,j2 = default_range(src,i1,j1,i2,j2) + local nr,nc = array2d.size(dest) + i2, j2 = min(nr,i2), min(nc,j2) + --i1, j1 = max(1,i1), max(1,j1) + dj = dj - 1 + for i = i1,i2 do + local drow, srow = dest[i+di-1], src[i] + for j = j1,j2 do + drow[j+dj] = srow[j] + end + end +end + +--- iterate over all elements in a 2D array, with optional indices. +-- @array2d a 2D array +-- @tparam {int} indices with indices (default false) +-- @int i1 start row (default 1) +-- @int j1 start col (default 1) +-- @int i2 end row (default N) +-- @int j2 end col (default M) +-- @return either value or i,j,value depending on indices +function array2d.iter (a,indices,i1,j1,i2,j2) + assert_arg(1,a,'table') + local norowset = not (i2 and j2) + i1,j1,i2,j2 = default_range(a,i1,j1,i2,j2) + local i,j = i1-1,j1-1 + local row,nr = nil,0 + local onr = j2 - j1 + 1 + return function() + j = j + 1 + if j > nr then + j = j1 + i = i + 1 + if i > i2 then return nil end + row = a[i] + nr = norowset and #row or onr + end + if indices then + return i,j,row[j] + else + return row[j] + end + end +end + +--- iterate over all columns. +-- @array2d a a 2D array +-- @return each column in turn +function array2d.columns (a) + assert_arg(1,a,'table') + local n = a[1][1] + local i = 0 + return function() + i = i + 1 + if i > n then return nil end + return column(a,i) + end +end + +--- new array of specified dimensions +-- @int rows number of rows +-- @int cols number of cols +-- @param val initial value; if it's a function then use `val(i,j)` +-- @return new 2d array +function array2d.new(rows,cols,val) + local res = {} + local fun = types.is_callable(val) + for i = 1,rows do + local row = {} + if fun then + for j = 1,cols do row[j] = val(i,j) end + else + for j = 1,cols do row[j] = val end + end + res[i] = row + end + return res +end + +return array2d + + diff --git a/framework/lualib/thirdparty/pl/class.lua b/framework/lualib/thirdparty/pl/class.lua new file mode 100755 index 0000000..49246ee --- /dev/null +++ b/framework/lualib/thirdparty/pl/class.lua @@ -0,0 +1,265 @@ +--- Provides a reuseable and convenient framework for creating classes in Lua. +-- Two possible notations: +-- +-- B = class(A) +-- class.B(A) +-- +-- The latter form creates a named class within the current environment. Note +-- that this implicitly brings in `pl.utils` as a dependency. +-- +-- See the Guide for further @{01-introduction.md.Simplifying_Object_Oriented_Programming_in_Lua|discussion} +-- @module pl.class + +local error, getmetatable, io, pairs, rawget, rawset, setmetatable, tostring, type = + _G.error, _G.getmetatable, _G.io, _G.pairs, _G.rawget, _G.rawset, _G.setmetatable, _G.tostring, _G.type +local compat + +-- this trickery is necessary to prevent the inheritance of 'super' and +-- the resulting recursive call problems. +local function call_ctor (c,obj,...) + local init = rawget(c,'_init') + local parent_with_init = rawget(c,'_parent_with_init') + + if parent_with_init then + if not init then -- inheriting an init + init = rawget(parent_with_init, '_init') + parent_with_init = rawget(parent_with_init, '_parent_with_init') + end + if parent_with_init then -- super() points to one above whereever _init came from + rawset(obj,'super',function(obj,...) + call_ctor(parent_with_init,obj,...) + end) + end + else + -- Without this, calling super() where none exists will sometimes loop and stack overflow + rawset(obj,'super',nil) + end + + local res = init(obj,...) + if parent_with_init then -- If this execution of call_ctor set a super, unset it + rawset(obj,'super',nil) + end + + return res +end + +--- initializes an __instance__ upon creation. +-- @function class:_init +-- @param ... parameters passed to the constructor +-- @usage local Cat = class() +-- function Cat:_init(name) +-- --self:super(name) -- call the ancestor initializer if needed +-- self.name = name +-- end +-- +-- local pussycat = Cat("pussycat") +-- print(pussycat.name) --> pussycat + +--- checks whether an __instance__ is derived from some class. +-- Works the other way around as `class_of`. It has two ways of using; +-- 1) call with a class to check against, 2) call without params. +-- @function instance:is_a +-- @param some_class class to check against, or `nil` to return the class +-- @return `true` if `instance` is derived from `some_class`, or if `some_class == nil` then +-- it returns the class table of the instance +-- @usage local pussycat = Lion() -- assuming Lion derives from Cat +-- if pussycat:is_a(Cat) then +-- -- it's true, it is a Lion, but also a Cat +-- end +-- +-- if pussycat:is_a() == Lion then +-- -- It's true +-- end +local function is_a(self,klass) + if klass == nil then + -- no class provided, so return the class this instance is derived from + return getmetatable(self) + end + local m = getmetatable(self) + if not m then return false end --*can't be an object! + while m do + if m == klass then return true end + m = rawget(m,'_base') + end + return false +end + +--- checks whether an __instance__ is derived from some class. +-- Works the other way around as `is_a`. +-- @function some_class:class_of +-- @param some_instance instance to check against +-- @return `true` if `some_instance` is derived from `some_class` +-- @usage local pussycat = Lion() -- assuming Lion derives from Cat +-- if Cat:class_of(pussycat) then +-- -- it's true +-- end +local function class_of(klass,obj) + if type(klass) ~= 'table' or not rawget(klass,'is_a') then return false end + return klass.is_a(obj,klass) +end + +--- cast an object to another class. +-- It is not clever (or safe!) so use carefully. +-- @param some_instance the object to be changed +-- @function some_class:cast +local function cast (klass, obj) + return setmetatable(obj,klass) +end + + +local function _class_tostring (obj) + local mt = obj._class + local name = rawget(mt,'_name') + setmetatable(obj,nil) + local str = tostring(obj) + setmetatable(obj,mt) + if name then str = name ..str:gsub('table','') end + return str +end + +local function tupdate(td,ts,dont_override) + for k,v in pairs(ts) do + if not dont_override or td[k] == nil then + td[k] = v + end + end +end + +local function _class(base,c_arg,c) + -- the class `c` will be the metatable for all its objects, + -- and they will look up their methods in it. + local mt = {} -- a metatable for the class to support __call and _handler + -- can define class by passing it a plain table of methods + local plain = type(base) == 'table' and not getmetatable(base) + if plain then + c = base + base = c._base + else + c = c or {} + end + + if type(base) == 'table' then + -- our new class is a shallow copy of the base class! + -- but be careful not to wipe out any methods we have been given at this point! + tupdate(c,base,plain) + c._base = base + -- inherit the 'not found' handler, if present + if rawget(c,'_handler') then mt.__index = c._handler end + elseif base ~= nil then + error("must derive from a table type",3) + end + + c.__index = c + setmetatable(c,mt) + if not plain then + if base and rawget(base,'_init') then c._parent_with_init = base end -- For super and inherited init + c._init = nil + end + + if base and rawget(base,'_class_init') then + base._class_init(c,c_arg) + end + + -- expose a ctor which can be called by () + mt.__call = function(class_tbl,...) + local obj + if rawget(c,'_create') then obj = c._create(...) end + if not obj then obj = {} end + setmetatable(obj,c) + + if rawget(c,'_init') or rawget(c,'_parent_with_init') then -- constructor exists + local res = call_ctor(c,obj,...) + if res then -- _if_ a ctor returns a value, it becomes the object... + obj = res + setmetatable(obj,c) + end + end + + if base and rawget(base,'_post_init') then + base._post_init(obj) + end + + return obj + end + -- Call Class.catch to set a handler for methods/properties not found in the class! + c.catch = function(self, handler) + if type(self) == "function" then + -- called using . instead of : + handler = self + end + c._handler = handler + mt.__index = handler + end + c.is_a = is_a + c.class_of = class_of + c.cast = cast + c._class = c + + if not rawget(c,'__tostring') then + c.__tostring = _class_tostring + end + + return c +end + +--- create a new class, derived from a given base class. +-- Supporting two class creation syntaxes: +-- either `Name = class(base)` or `class.Name(base)`. +-- The first form returns the class directly and does not set its `_name`. +-- The second form creates a variable `Name` in the current environment set +-- to the class, and also sets `_name`. +-- @function class +-- @param base optional base class +-- @param c_arg optional parameter to class constructor +-- @param c optional table to be used as class +local class +class = setmetatable({},{ + __call = function(fun,...) + return _class(...) + end, + __index = function(tbl,key) + if key == 'class' then + io.stderr:write('require("pl.class").class is deprecated. Use require("pl.class")\n') + return class + end + compat = compat or require 'pl.compat' + local env = compat.getfenv(2) + return function(...) + local c = _class(...) + c._name = key + rawset(env,key,c) + return c + end + end +}) + +class.properties = class() + +function class.properties._class_init(klass) + klass.__index = function(t,key) + -- normal class lookup! + local v = klass[key] + if v then return v end + -- is it a getter? + v = rawget(klass,'get_'..key) + if v then + return v(t) + end + -- is it a field? + return rawget(t,'_'..key) + end + klass.__newindex = function (t,key,value) + -- if there's a setter, use that, otherwise directly set table + local p = 'set_'..key + local setter = klass[p] + if setter then + setter(t,value) + else + rawset(t,key,value) + end + end +end + + +return class + diff --git a/framework/lualib/thirdparty/pl/compat.lua b/framework/lualib/thirdparty/pl/compat.lua new file mode 100755 index 0000000..1707ef6 --- /dev/null +++ b/framework/lualib/thirdparty/pl/compat.lua @@ -0,0 +1,250 @@ +---------------- +--- Lua 5.1/5.2/5.3 compatibility. +-- Injects `table.pack`, `table.unpack`, and `package.searchpath` in the global +-- environment, to make sure they are available for Lua 5.1 and LuaJIT. +-- +-- All other functions are exported as usual in the returned module table. +-- +-- NOTE: everything in this module is also available in `pl.utils`. +-- @module pl.compat +local compat = {} + +--- boolean flag this is Lua 5.1 (or LuaJIT). +-- @field lua51 +compat.lua51 = _VERSION == 'Lua 5.1' + +--- boolean flag this is LuaJIT. +-- @field jit +compat.jit = (tostring(assert):match('builtin') ~= nil) + +--- boolean flag this is LuaJIT with 5.2 compatibility compiled in. +-- @field jit52 +if compat.jit then + -- 'goto' is a keyword when 52 compatibility is enabled in LuaJit + compat.jit52 = not loadstring("local goto = 1") +end + +--- the directory separator character for the current platform. +-- @field dir_separator +compat.dir_separator = _G.package.config:sub(1,1) + +--- boolean flag this is a Windows platform. +-- @field is_windows +compat.is_windows = compat.dir_separator == '\\' + +--- execute a shell command, in a compatible and platform independent way. +-- This is a compatibility function that returns the same for Lua 5.1 and +-- Lua 5.2+. +-- +-- NOTE: Windows systems can use signed 32bit integer exitcodes. Posix systems +-- only use exitcodes 0-255, anything else is undefined. +-- +-- NOTE2: In Lua 5.2 and 5.3 a Windows exitcode of -1 would not properly be +-- returned, this function will return it properly for all versions. +-- @param cmd a shell command +-- @return true if successful +-- @return actual return code +function compat.execute(cmd) + local res1,res2,res3 = os.execute(cmd) + if res2 == "No error" and res3 == 0 and compat.is_windows then + -- os.execute bug in Lua 5.2/5.3 not reporting -1 properly on Windows + -- this was fixed in 5.4 + res3 = -1 + end + if compat.lua51 and not compat.jit52 then + if compat.is_windows then + return res1==0,res1 + else + res1 = res1 > 255 and res1 / 256 or res1 + return res1==0,res1 + end + else + if compat.is_windows then + return res3==0,res3 + else + return not not res1,res3 + end + end +end + +---------------- +-- Load Lua code as a text or binary chunk (in a Lua 5.2 compatible way). +-- @param ld code string or loader +-- @param[opt] source name of chunk for errors +-- @param[opt] mode 'b', 't' or 'bt' +-- @param[opt] env environment to load the chunk in +-- @function compat.load + +--------------- +-- Get environment of a function (in a Lua 5.1 compatible way). +-- Not 100% compatible, so with Lua 5.2 it may return nil for a function with no +-- global references! +-- Based on code by [Sergey Rozhenko](http://lua-users.org/lists/lua-l/2010-06/msg00313.html) +-- @param f a function or a call stack reference +-- @function compat.getfenv + +--------------- +-- Set environment of a function (in a Lua 5.1 compatible way). +-- @param f a function or a call stack reference +-- @param env a table that becomes the new environment of `f` +-- @function compat.setfenv + +if compat.lua51 then -- define Lua 5.2 style load() + if not compat.jit then -- but LuaJIT's load _is_ compatible + local lua51_load = load + function compat.load(str,src,mode,env) + local chunk,err + if type(str) == 'string' then + if str:byte(1) == 27 and not (mode or 'bt'):find 'b' then + return nil,"attempt to load a binary chunk" + end + chunk,err = loadstring(str,src) + else + chunk,err = lua51_load(str,src) + end + if chunk and env then setfenv(chunk,env) end + return chunk,err + end + else + compat.load = load + end + compat.setfenv, compat.getfenv = setfenv, getfenv +else + compat.load = load + -- setfenv/getfenv replacements for Lua 5.2 + -- by Sergey Rozhenko + -- http://lua-users.org/lists/lua-l/2010-06/msg00313.html + -- Roberto Ierusalimschy notes that it is possible for getfenv to return nil + -- in the case of a function with no globals: + -- http://lua-users.org/lists/lua-l/2010-06/msg00315.html + function compat.setfenv(f, t) + f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func) + local name + local up = 0 + repeat + up = up + 1 + name = debug.getupvalue(f, up) + until name == '_ENV' or name == nil + if name then + debug.upvaluejoin(f, up, function() return name end, 1) -- use unique upvalue + debug.setupvalue(f, up, t) + end + if f ~= 0 then return f end + end + + function compat.getfenv(f) + local f = f or 0 + f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func) + local name, val + local up = 0 + repeat + up = up + 1 + name, val = debug.getupvalue(f, up) + until name == '_ENV' or name == nil + return val + end +end + + +--- Global exported functions (for Lua 5.1 & LuaJIT) +-- @section lua52 + +--- pack an argument list into a table. +-- @param ... any arguments +-- @return a table with field n set to the length +-- @function table.pack +if not table.pack then + function table.pack (...) -- luacheck: ignore + return {n=select('#',...); ...} + end +end + +--- unpack a table and return the elements. +-- +-- NOTE: this version does NOT honor the n field, and hence it is not nil-safe. +-- See `utils.unpack` for a version that is nil-safe. +-- @param t table to unpack +-- @param[opt] i index from which to start unpacking, defaults to 1 +-- @param[opt] t index of the last element to unpack, defaults to #t +-- @return multiple return values from the table +-- @function table.unpack +-- @see utils.unpack +if not table.unpack then + table.unpack = unpack -- luacheck: ignore +end + +--- return the full path where a file name would be matched. +-- This function was introduced in Lua 5.2, so this compatibility version +-- will be injected in Lua 5.1 engines. +-- @string name file name, possibly dotted +-- @string path a path-template in the same form as package.path or package.cpath +-- @string[opt] sep template separate character to be replaced by path separator. Default: "." +-- @string[opt] rep the path separator to use, defaults to system separator. Default; "/" on Unixes, "\" on Windows. +-- @see path.package_path +-- @function package.searchpath +-- @return on success: path of the file +-- @return on failure: nil, error string listing paths tried +if not package.searchpath then + function package.searchpath (name,path,sep,rep) -- luacheck: ignore + if type(name) ~= "string" then + error(("bad argument #1 to 'searchpath' (string expected, got %s)"):format(type(path)), 2) + end + if type(path) ~= "string" then + error(("bad argument #2 to 'searchpath' (string expected, got %s)"):format(type(path)), 2) + end + if sep ~= nil and type(sep) ~= "string" then + error(("bad argument #3 to 'searchpath' (string expected, got %s)"):format(type(path)), 2) + end + if rep ~= nil and type(rep) ~= "string" then + error(("bad argument #4 to 'searchpath' (string expected, got %s)"):format(type(path)), 2) + end + sep = sep or "." + rep = rep or compat.dir_separator + do + local s, e = name:find(sep, nil, true) + while s do + name = name:sub(1, s-1) .. rep .. name:sub(e+1, -1) + s, e = name:find(sep, s + #rep + 1, true) + end + end + local tried = {} + for m in path:gmatch('[^;]+') do + local nm = m:gsub('?', name) + tried[#tried+1] = nm + local f = io.open(nm,'r') + if f then f:close(); return nm end + end + return nil, "\tno file '" .. table.concat(tried, "'\n\tno file '") .. "'" + end +end + +--- Global exported functions (for Lua < 5.4) +-- @section lua54 + +--- raise a warning message. +-- This functions mimics the `warn` function added in Lua 5.4. +-- @function warn +-- @param ... any arguments +if not warn then -- luacheck: ignore + local enabled = false + function warn(arg1, ...) -- luacheck: ignore + if type(arg1) == "string" and arg1:sub(1, 1) == "@" then + -- control message + if arg1 == "@on" then + enabled = true + return + end + if arg1 == "@off" then + enabled = false + return + end + return -- ignore unknown control messages + end + if enabled then + io.stderr:write("Lua warning: ", arg1, ...) + io.stderr:write("\n") + end + end +end + +return compat diff --git a/framework/lualib/thirdparty/pl/comprehension.lua b/framework/lualib/thirdparty/pl/comprehension.lua new file mode 100755 index 0000000..39be7c5 --- /dev/null +++ b/framework/lualib/thirdparty/pl/comprehension.lua @@ -0,0 +1,285 @@ +--- List comprehensions implemented in Lua. +-- +-- See the [wiki page](http://lua-users.org/wiki/ListComprehensions) +-- +-- local C= require 'pl.comprehension' . new() +-- +-- C ('x for x=1,10') () +-- ==> {1,2,3,4,5,6,7,8,9,10} +-- C 'x^2 for x=1,4' () +-- ==> {1,4,9,16} +-- C '{x,x^2} for x=1,4' () +-- ==> {{1,1},{2,4},{3,9},{4,16}} +-- C '2*x for x' {1,2,3} +-- ==> {2,4,6} +-- dbl = C '2*x for x' +-- dbl {10,20,30} +-- ==> {20,40,60} +-- C 'x for x if x % 2 == 0' {1,2,3,4,5} +-- ==> {2,4} +-- C '{x,y} for x = 1,2 for y = 1,2' () +-- ==> {{1,1},{1,2},{2,1},{2,2}} +-- C '{x,y} for x for y' ({1,2},{10,20}) +-- ==> {{1,10},{1,20},{2,10},{2,20}} +-- assert(C 'sum(x^2 for x)' {2,3,4} == 2^2+3^2+4^2) +-- +-- (c) 2008 David Manura. Licensed under the same terms as Lua (MIT license). +-- +-- Dependencies: `pl.utils`, `pl.luabalanced` +-- +-- See @{07-functional.md.List_Comprehensions|the Guide} +-- @module pl.comprehension + +local utils = require 'pl.utils' + +local status,lb = pcall(require, "pl.luabalanced") +if not status then + lb = require 'luabalanced' +end + +local math_max = math.max +local table_concat = table.concat + +-- fold operations +-- http://en.wikipedia.org/wiki/Fold_(higher-order_function) +local ops = { + list = {init=' {} ', accum=' __result[#__result+1] = (%s) '}, + table = {init=' {} ', accum=' local __k, __v = %s __result[__k] = __v '}, + sum = {init=' 0 ', accum=' __result = __result + (%s) '}, + min = {init=' nil ', accum=' local __tmp = %s ' .. + ' if __result then if __tmp < __result then ' .. + '__result = __tmp end else __result = __tmp end '}, + max = {init=' nil ', accum=' local __tmp = %s ' .. + ' if __result then if __tmp > __result then ' .. + '__result = __tmp end else __result = __tmp end '}, +} + + +-- Parses comprehension string expr. +-- Returns output expression list string, array of for types +-- ('=', 'in' or nil) , array of input variable name +-- strings , array of input variable value strings +-- , array of predicate expression strings , +-- operation name string , and number of placeholder +-- parameters . +-- +-- The is equivalent to the mathematical set-builder notation: +-- +-- { | in , } +-- +-- @usage "x^2 for x" -- array values +-- @usage "x^2 for x=1,10,2" -- numeric for +-- @usage "k^v for k,v in pairs(_1)" -- iterator for +-- @usage "(x+y)^2 for x for y if x > y" -- nested +-- +local function parse_comprehension(expr) + local pos = 1 + + -- extract opname (if exists) + local opname + local tok, post = expr:match('^%s*([%a_][%w_]*)%s*%(()', pos) + local pose = #expr + 1 + if tok then + local tok2, posb = lb.match_bracketed(expr, post-1) + assert(tok2, 'syntax error') + if expr:match('^%s*$', posb) then + opname = tok + pose = posb - 1 + pos = post + end + end + opname = opname or "list" + + -- extract out expression list + local out; out, pos = lb.match_explist(expr, pos) + assert(out, "syntax error: missing expression list") + out = table_concat(out, ', ') + + -- extract "for" clauses + local fortypes = {} + local invarlists = {} + local invallists = {} + while 1 do + local post = expr:match('^%s*for%s+()', pos) + if not post then break end + pos = post + + -- extract input vars + local iv; iv, pos = lb.match_namelist(expr, pos) + assert(#iv > 0, 'syntax error: zero variables') + for _,ident in ipairs(iv) do + assert(not ident:match'^__', + "identifier " .. ident .. " may not contain __ prefix") + end + invarlists[#invarlists+1] = iv + + -- extract '=' or 'in' (optional) + local fortype, post = expr:match('^(=)%s*()', pos) + if not fortype then fortype, post = expr:match('^(in)%s+()', pos) end + if fortype then + pos = post + -- extract input value range + local il; il, pos = lb.match_explist(expr, pos) + assert(#il > 0, 'syntax error: zero expressions') + assert(fortype ~= '=' or #il == 2 or #il == 3, + 'syntax error: numeric for requires 2 or three expressions') + fortypes[#invarlists] = fortype + invallists[#invarlists] = il + else + fortypes[#invarlists] = false + invallists[#invarlists] = false + end + end + assert(#invarlists > 0, 'syntax error: missing "for" clause') + + -- extract "if" clauses + local preds = {} + while 1 do + local post = expr:match('^%s*if%s+()', pos) + if not post then break end + pos = post + local pred; pred, pos = lb.match_expression(expr, pos) + assert(pred, 'syntax error: predicated expression not found') + preds[#preds+1] = pred + end + + -- extract number of parameter variables (name matching "_%d+") + local stmp = ''; lb.gsub(expr, function(u, sin) -- strip comments/strings + if u == 'e' then stmp = stmp .. ' ' .. sin .. ' ' end + end) + local max_param = 0; stmp:gsub('[%a_][%w_]*', function(s) + local s = s:match('^_(%d+)$') + if s then max_param = math_max(max_param, tonumber(s)) end + end) + + if pos ~= pose then + assert(false, "syntax error: unrecognized " .. expr:sub(pos)) + end + + --DEBUG: + --print('----\n', string.format("%q", expr), string.format("%q", out), opname) + --for k,v in ipairs(invarlists) do print(k,v, invallists[k]) end + --for k,v in ipairs(preds) do print(k,v) end + + return out, fortypes, invarlists, invallists, preds, opname, max_param +end + + +-- Create Lua code string representing comprehension. +-- Arguments are in the form returned by parse_comprehension. +local function code_comprehension( + out, fortypes, invarlists, invallists, preds, opname, max_param +) + local op = assert(ops[opname]) + local code = op.accum:gsub('%%s', out) + + for i=#preds,1,-1 do local pred = preds[i] + code = ' if ' .. pred .. ' then ' .. code .. ' end ' + end + for i=#invarlists,1,-1 do + if not fortypes[i] then + local arrayname = '__in' .. i + local idx = '__idx' .. i + code = + ' for ' .. idx .. ' = 1, #' .. arrayname .. ' do ' .. + ' local ' .. invarlists[i][1] .. ' = ' .. arrayname .. '['..idx..'] ' .. + code .. ' end ' + else + code = + ' for ' .. + table_concat(invarlists[i], ', ') .. + ' ' .. fortypes[i] .. ' ' .. + table_concat(invallists[i], ', ') .. + ' do ' .. code .. ' end ' + end + end + code = ' local __result = ( ' .. op.init .. ' ) ' .. code + return code +end + + +-- Convert code string represented by code_comprehension +-- into Lua function. Also must pass ninputs = #invarlists, +-- max_param, and invallists (from parse_comprehension). +-- Uses environment env. +local function wrap_comprehension(code, ninputs, max_param, invallists, env) + assert(ninputs > 0) + local ts = {} + for i=1,max_param do + ts[#ts+1] = '_' .. i + end + for i=1,ninputs do + if not invallists[i] then + local name = '__in' .. i + ts[#ts+1] = name + end + end + if #ts > 0 then + code = ' local ' .. table_concat(ts, ', ') .. ' = ... ' .. code + end + code = code .. ' return __result ' + --print('DEBUG:', code) + local f, err = utils.load(code,'tmp','t',env) + if not f then assert(false, err .. ' with generated code ' .. code) end + return f +end + + +-- Build Lua function from comprehension string. +-- Uses environment env. +local function build_comprehension(expr, env) + local out, fortypes, invarlists, invallists, preds, opname, max_param + = parse_comprehension(expr) + local code = code_comprehension( + out, fortypes, invarlists, invallists, preds, opname, max_param) + local f = wrap_comprehension(code, #invarlists, max_param, invallists, env) + return f +end + + +-- Creates new comprehension cache. +-- Any list comprehension function created are set to the environment +-- env (defaults to caller of new). +local function new(env) + -- Note: using a single global comprehension cache would have had + -- security implications (e.g. retrieving cached functions created + -- in other environments). + -- The cache lookup function could have instead been written to retrieve + -- the caller's environment, lookup up the cache private to that + -- environment, and then looked up the function in that cache. + -- That would avoid the need for this call to + -- explicitly manage caches; however, that might also have an undue + -- performance penalty. + + if not env then + env = utils.getfenv(2) + end + + local mt = {} + local cache = setmetatable({}, mt) + + -- Index operator builds, caches, and returns Lua function + -- corresponding to comprehension expression string. + -- + -- Example: f = comprehension['x^2 for x'] + -- + function mt:__index(expr) + local f = build_comprehension(expr, env) + self[expr] = f -- cache + return f + end + + -- Convenience syntax. + -- Allows comprehension 'x^2 for x' instead of comprehension['x^2 for x']. + mt.__call = mt.__index + + cache.new = new + + return cache +end + + +local comprehension = {} +comprehension.new = new + +return comprehension diff --git a/framework/lualib/thirdparty/pl/config.lua b/framework/lualib/thirdparty/pl/config.lua new file mode 100755 index 0000000..2e6db0c --- /dev/null +++ b/framework/lualib/thirdparty/pl/config.lua @@ -0,0 +1,207 @@ +--- Reads configuration files into a Lua table. +-- Understands INI files, classic Unix config files, and simple +-- delimited columns of values. See @{06-data.md.Reading_Configuration_Files|the Guide} +-- +-- # test.config +-- # Read timeout in seconds +-- read.timeout=10 +-- # Write timeout in seconds +-- write.timeout=5 +-- #acceptable ports +-- ports = 1002,1003,1004 +-- +-- -- readconfig.lua +-- local config = require 'config' +-- local t = config.read 'test.config' +-- print(pretty.write(t)) +-- +-- ### output ##### +-- { +-- ports = { +-- 1002, +-- 1003, +-- 1004 +-- }, +-- write_timeout = 5, +-- read_timeout = 10 +-- } +-- +-- @module pl.config + +local type,tonumber,ipairs,io, table = _G.type,_G.tonumber,_G.ipairs,_G.io,_G.table + +local function split(s,re) + local res = {} + local t_insert = table.insert + re = '[^'..re..']+' + for k in s:gmatch(re) do t_insert(res,k) end + return res +end + +local function strip(s) + return s:gsub('^%s+',''):gsub('%s+$','') +end + +local function strip_quotes (s) + return s:gsub("['\"](.*)['\"]",'%1') +end + +local config = {} + +--- like io.lines(), but allows for lines to be continued with '\'. +-- @param file a file-like object (anything where read() returns the next line) or a filename. +-- Defaults to stardard input. +-- @return an iterator over the lines, or nil +-- @return error 'not a file-like object' or 'file is nil' +function config.lines(file) + local f,openf,err + local line = '' + if type(file) == 'string' then + f,err = io.open(file,'r') + if not f then return nil,err end + openf = true + else + f = file or io.stdin + if not file.read then return nil, 'not a file-like object' end + end + if not f then return nil, 'file is nil' end + return function() + local l = f:read() + while l do + -- only for non-blank lines that don't begin with either ';' or '#' + if l:match '%S' and not l:match '^%s*[;#]' then + -- does the line end with '\'? + local i = l:find '\\%s*$' + if i then -- if so, + line = line..l:sub(1,i-1) + elseif line == '' then + return l + else + l = line..l + line = '' + return l + end + end + l = f:read() + end + if openf then f:close() end + end +end + +--- read a configuration file into a table +-- @param file either a file-like object or a string, which must be a filename +-- @tab[opt] cnfg a configuration table that may contain these fields: +-- +-- * `smart` try to deduce what kind of config file we have (default false) +-- * `variabilize` make names into valid Lua identifiers (default true) +-- * `convert_numbers` try to convert values into numbers (default true) +-- * `trim_space` ensure that there is no starting or trailing whitespace with values (default true) +-- * `trim_quotes` remove quotes from strings (default false) +-- * `list_delim` delimiter to use when separating columns (default ',') +-- * `keysep` separator between key and value pairs (default '=') +-- +-- @return a table containing items, or `nil` +-- @return error message (same as @{config.lines} +function config.read(file,cnfg) + local auto + + local iter,err = config.lines(file) + if not iter then return nil,err end + local line = iter() + cnfg = cnfg or {} + if cnfg.smart then + auto = true + if line:match '^[^=]+=' then + cnfg.keysep = '=' + elseif line:match '^[^:]+:' then + cnfg.keysep = ':' + cnfg.list_delim = ':' + elseif line:match '^%S+%s+' then + cnfg.keysep = ' ' + -- more than two columns assume that it's a space-delimited list + -- cf /etc/fstab with /etc/ssh/ssh_config + if line:match '^%S+%s+%S+%s+%S+' then + cnfg.list_delim = ' ' + end + cnfg.variabilize = false + end + end + + + local function check_cnfg (var,def) + local val = cnfg[var] + if val == nil then return def else return val end + end + + local initial_digits = '^[%d%+%-]' + local t = {} + local top_t = t + local variabilize = check_cnfg ('variabilize',true) + local list_delim = check_cnfg('list_delim',',') + local convert_numbers = check_cnfg('convert_numbers',true) + local convert_boolean = check_cnfg('convert_boolean',false) + local trim_space = check_cnfg('trim_space',true) + local trim_quotes = check_cnfg('trim_quotes',false) + local ignore_assign = check_cnfg('ignore_assign',false) + local keysep = check_cnfg('keysep','=') + local keypat = keysep == ' ' and '%s+' or '%s*'..keysep..'%s*' + if list_delim == ' ' then list_delim = '%s+' end + + local function process_name(key) + if variabilize then + key = key:gsub('[^%w]','_') + end + return key + end + + local function process_value(value) + if list_delim and value:find(list_delim) then + value = split(value,list_delim) + for i,v in ipairs(value) do + value[i] = process_value(v) + end + elseif convert_numbers and value:find(initial_digits) then + local val = tonumber(value) + if not val and value:match ' kB$' then + value = value:gsub(' kB','') + val = tonumber(value) + end + if val then value = val end + elseif convert_boolean and value == 'true' then + return true + elseif convert_boolean and value == 'false' then + return false + end + if type(value) == 'string' then + if trim_space then value = strip(value) end + if not trim_quotes and auto and value:match '^"' then + trim_quotes = true + end + if trim_quotes then value = strip_quotes(value) end + end + return value + end + + while line do + if line:find('^%[') then -- section! + local section = process_name(line:match('%[([^%]]+)%]')) + t = top_t + t[section] = {} + t = t[section] + else + line = line:gsub('^%s*','') + local i1,i2 = line:find(keypat) + if i1 and not ignore_assign then -- key,value assignment + local key = process_name(line:sub(1,i1-1)) + local value = process_value(line:sub(i2+1)) + t[key] = value + else -- a plain list of values... + t[#t+1] = process_value(line) + end + end + line = iter() + end + return top_t +end + +return config diff --git a/framework/lualib/thirdparty/pl/data.lua b/framework/lualib/thirdparty/pl/data.lua new file mode 100755 index 0000000..a565ebc --- /dev/null +++ b/framework/lualib/thirdparty/pl/data.lua @@ -0,0 +1,654 @@ +--- Reading and querying simple tabular data. +-- +-- data.read 'test.txt' +-- ==> {{10,20},{2,5},{40,50},fieldnames={'x','y'},delim=','} +-- +-- Provides a way of creating basic SQL-like queries. +-- +-- require 'pl' +-- local d = data.read('xyz.txt') +-- local q = d:select('x,y,z where x > 3 and z < 2 sort by y') +-- for x,y,z in q do +-- print(x,y,z) +-- end +-- +-- See @{06-data.md.Reading_Columnar_Data|the Guide} +-- +-- Dependencies: `pl.utils`, `pl.array2d` (fallback methods) +-- @module pl.data + +local utils = require 'pl.utils' +local _DEBUG = rawget(_G,'_DEBUG') + +local patterns,function_arg,usplit,array_tostring = utils.patterns,utils.function_arg,utils.split,utils.array_tostring +local append,concat = table.insert,table.concat +local gsub = string.gsub +local io = io +local _G,print,type,tonumber,ipairs,setmetatable = _G,print,type,tonumber,ipairs,setmetatable + + +local data = {} + +local parse_select + +local function rstrip(s) + return (s:gsub('%s+$','')) +end + +local function strip (s) + return (rstrip(s):gsub('^%s*','')) +end + +-- This gives `l` the standard List metatable, +-- pulling in the List module. +local function makelist(l) + return setmetatable(l, require('pl.List')) +end + +local function map(fun,t) + local res = {} + for i = 1,#t do + res[i] = fun(t[i]) + end + return res +end + +local function split(line,delim,csv,n) + local massage + -- CSV fields may be double-quoted and may contain commas! + if csv and line:match '"' then + line = line:gsub('"([^"]+)"',function(str) + local s,cnt = str:gsub(',','\001') + if cnt > 0 then massage = true end + return s + end) + if massage then + massage = function(s) return (s:gsub('\001',',')) end + end + end + local res = (usplit(line,delim,false,n)) + if csv then + -- restore CSV commas-in-fields + if massage then res = map(massage,res) end + -- in CSV mode trailiing commas are significant! + if line:match ',$' then append(res,'') end + end + return makelist(res) +end + +local function find(t,v) + for i = 1,#t do + if v == t[i] then return i end + end +end + +local DataMT = { + column_by_name = function(self,name) + if type(name) == 'number' then + name = '$'..name + end + local arr = {} + for res in data.query(self,name) do + append(arr,res) + end + return makelist(arr) + end, + + copy_select = function(self,condn) + condn = parse_select(condn,self) + local iter = data.query(self,condn) + local res = {} + local row = makelist{iter()} + while #row > 0 do + append(res,row) + row = makelist{iter()} + end + res.delim = self.delim + return data.new(res,split(condn.fields,',')) + end, + + column_names = function(self) + return self.fieldnames + end, +} + +local array2d + +DataMT.__index = function(self,name) + local f = DataMT[name] + if f then return f end + if not array2d then + array2d = require 'pl.array2d' + end + return array2d[name] +end + +--- return a particular column as a list of values (method). +-- @param name either name of column, or numerical index. +-- @function Data.column_by_name + +--- return a query iterator on this data (method). +-- @string condn the query expression +-- @function Data.select +-- @see data.query + +--- return a row iterator on this data (method). +-- @string condn the query expression +-- @function Data.select_row + +--- return a new data object based on this query (method). +-- @string condn the query expression +-- @function Data.copy_select + +--- return the field names of this data object (method). +-- @function Data.column_names + +--- write out a row (method). +-- @param f file-like object +-- @function Data.write_row + +--- write data out to file (method). +-- @param f file-like object +-- @function Data.write + + +-- [guessing delimiter] We check for comma, tab and spaces in that order. +-- [issue] any other delimiters to be checked? +local delims = {',', '\t', ' ', ';'} + +local function guess_delim (line) + if line=='' then return ' ' end + for _,delim in ipairs(delims) do + if line:find(delim) then + return delim == ' ' and '%s+' or delim + end + end + return ' ' +end + +-- [file parameter] If it's a string, we try open as a filename. If nil, then +-- either stdin or stdout depending on the mode. Otherwise, check if this is +-- a file-like object (implements read or write depending) +local function open_file (f,mode) + local opened, err + local reading = mode == 'r' + if type(f) == 'string' then + if f == 'stdin' then + f = io.stdin + elseif f == 'stdout' then + f = io.stdout + else + f,err = io.open(f,mode) + if not f then return nil,err end + opened = true + end + end + if f and ((reading and not f.read) or (not reading and not f.write)) then + return nil, "not a file-like object" + end + return f,nil,opened +end + +--- read a delimited file in a Lua table. +-- By default, attempts to treat first line as separated list of fieldnames. +-- @param file a filename or a file-like object +-- @tab cnfg parsing options +-- @string cnfg.delim a string pattern to split fields +-- @array cnfg.fieldnames (i.e. don't read from first line) +-- @bool cnfg.no_convert (default is to try conversion on first data line) +-- @tab cnfg.convert table of custom conversion functions with column keys +-- @int cnfg.numfields indices of columns known to be numbers +-- @bool cnfg.last_field_collect only split as many fields as fieldnames. +-- @int cnfg.thousands_dot thousands separator in Excel CSV is '.' +-- @bool cnfg.csv fields may be double-quoted and contain commas; +-- Also, empty fields are considered to be equivalent to zero. +-- @return `data` object, or `nil` +-- @return error message. May be a file error, 'not a file-like object' +-- or a conversion error +function data.read(file,cnfg) + local count,line + local D = {} + if not cnfg then cnfg = {} end + local f,err,opened = open_file(file,'r') + if not f then return nil, err end + local thousands_dot = cnfg.thousands_dot + local csv = cnfg.csv + if csv then cnfg.delim = ',' end + + -- note that using dot as the thousands separator (@thousands_dot) + -- requires a special conversion function! For CSV, _empty fields_ are + -- considered to default to numerial zeroes. + local tonumber = tonumber + local function try_number(x) + if thousands_dot then x = x:gsub('%.(...)','%1') end + if csv and x == '' then x = '0' end + local v = tonumber(x) + if v == nil then return nil,"not a number" end + return v + end + + count = 1 + line = f:read() + if not line then return nil, "empty file" end + + -- first question: what is the delimiter? + D.delim = cnfg.delim and cnfg.delim or guess_delim(line) + local delim = D.delim + + local conversion + local numfields = {} + local function append_conversion (idx,conv) + conversion = conversion or {} + append(numfields,idx) + append(conversion,conv) + end + if cnfg.numfields then + for _,n in ipairs(cnfg.numfields) do append_conversion(n,try_number) end + end + + -- some space-delimited data starts with a space. This should not be a column, + -- although it certainly would be for comma-separated, etc. + local stripper + if delim == '%s+' and line:find(delim) == 1 then + stripper = function(s) return s:gsub('^%s+','') end + line = stripper(line) + end + -- first line will usually be field names. Unless fieldnames are specified, + -- we check if it contains purely numerical values for the case of reading + -- plain data files. + if not cnfg.fieldnames then + local fields,nums + fields = split(line,delim,csv) + if not cnfg.convert then + nums = map(tonumber,fields) + if #nums == #fields then -- they're ALL numbers! + append(D,nums) -- add the first converted row + -- and specify conversions for subsequent rows + for i = 1,#nums do append_conversion(i,try_number) end + else -- we'll try to check numbers just now.. + nums = nil + end + else -- [explicit column conversions] (any deduced number conversions will be added) + for idx,conv in pairs(cnfg.convert) do append_conversion(idx,conv) end + end + if nums == nil then + cnfg.fieldnames = fields + end + line = f:read() + count = count + 1 + if stripper then line = stripper(line) end + elseif type(cnfg.fieldnames) == 'string' then + cnfg.fieldnames = split(cnfg.fieldnames,delim,csv) + end + local nfields + -- at this point, the column headers have been read in. If the first + -- row consisted of numbers, it has already been added to the dataset. + if cnfg.fieldnames then + D.fieldnames = cnfg.fieldnames + -- [collecting end field] If @last_field_collect then we'll + -- only split as many fields as there are fieldnames + if cnfg.last_field_collect then + nfields = #D.fieldnames + end + -- [implicit column conversion] unless @no_convert, we need the numerical field indices + -- of the first data row. These can also be specified explicitly by @numfields. + if not cnfg.no_convert then + local fields = split(line,D.delim,csv,nfields) + for i = 1,#fields do + if not find(numfields,i) and try_number(fields[i]) then + append_conversion(i,try_number) + end + end + end + end + -- keep going until finished + while line do + if not line:find ('^%s*$') then -- [blank lines] ignore them! + if stripper then line = stripper(line) end + local fields = split(line,delim,csv,nfields) + if conversion then -- there were field conversions... + for k = 1,#numfields do + local i,conv = numfields[k],conversion[k] + local val,err = conv(fields[i]) + if val == nil then + return nil, err..": "..fields[i].." at line "..count + else + fields[i] = val + end + end + end + append(D,fields) + end + line = f:read() + count = count + 1 + end + if opened then f:close() end + if delim == '%s+' then D.delim = ' ' end + if not D.fieldnames then D.fieldnames = {} end + return data.new(D) +end + +local function write_row (data,f,row,delim) + data.temp = array_tostring(row,data.temp) + f:write(concat(data.temp,delim),'\n') +end + +function DataMT:write_row(f,row) + write_row(self,f,row,self.delim) +end + +--- write 2D data to a file. +-- Does not assume that the data has actually been +-- generated with `new` or `read`. +-- @param data 2D array +-- @param file filename or file-like object +-- @tparam[opt] {string} fieldnames list of fields (optional) +-- @string[opt='\t'] delim delimiter (default tab) +-- @return true or nil, error +function data.write (data,file,fieldnames,delim) + local f,err,opened = open_file(file,'w') + if not f then return nil, err end + if not fieldnames then + fieldnames = data.fieldnames + end + delim = delim or '\t' + if fieldnames and #fieldnames > 0 then + f:write(concat(fieldnames,delim),'\n') + end + for i = 1,#data do + write_row(data,f,data[i],delim) + end + if opened then f:close() end + return true +end + + +function DataMT:write(file) + data.write(self,file,self.fieldnames,self.delim) +end + +local function massage_fieldnames (fields,copy) + -- fieldnames must be valid Lua identifiers; ignore any surrounding padding + -- but keep the original fieldnames... + for i = 1,#fields do + local f = strip(fields[i]) + copy[i] = f + fields[i] = f:gsub('%W','_') + end +end + +--- create a new dataset from a table of rows. +-- Can specify the fieldnames, else the table must have a field called +-- 'fieldnames', which is either a string of delimiter-separated names, +-- or a table of names.
+-- If the table does not have a field called 'delim', then an attempt will be +-- made to guess it from the fieldnames string, defaults otherwise to tab. +-- @param d the table. +-- @tparam[opt] {string} fieldnames optional fieldnames +-- @return the table. +function data.new (d,fieldnames) + d.fieldnames = d.fieldnames or fieldnames or '' + if not d.delim and type(d.fieldnames) == 'string' then + d.delim = guess_delim(d.fieldnames) + d.fieldnames = split(d.fieldnames,d.delim) + end + d.fieldnames = makelist(d.fieldnames) + d.original_fieldnames = {} + massage_fieldnames(d.fieldnames,d.original_fieldnames) + setmetatable(d,DataMT) + -- a query with just the fieldname will return a sequence + -- of values, which seq.copy turns into a table. + return d +end + +local sorted_query = [[ +return function (t) + local i = 0 + local v + local ls = {} + for i,v in ipairs(t) do + if CONDITION then + ls[#ls+1] = v + end + end + table.sort(ls,function(v1,v2) + return SORT_EXPR + end) + local n = #ls + return function() + i = i + 1 + v = ls[i] + if i > n then return end + return FIELDLIST + end +end +]] + +-- question: is this optimized case actually worth the extra code? +local simple_query = [[ +return function (t) + local n = #t + local i = 0 + local v + return function() + repeat + i = i + 1 + v = t[i] + until i > n or CONDITION + if i > n then return end + return FIELDLIST + end +end +]] + +local function is_string (s) + return type(s) == 'string' +end + +local field_error + +local function fieldnames_as_string (data) + return concat(data.fieldnames,',') +end + +local function massage_fields(data,f) + local idx + if f:find '^%d+$' then + idx = tonumber(f) + else + idx = find(data.fieldnames,f) + end + if idx then + return 'v['..idx..']' + else + field_error = f..' not found in '..fieldnames_as_string(data) + return f + end +end + + +local function process_select (data,parms) + --- preparing fields ---- + field_error = nil + local fields = parms.fields + local numfields = fields:find '%$' or #data.fieldnames == 0 + if fields:find '^%s*%*%s*' then + if not numfields then + fields = fieldnames_as_string(data) + else + local ncol = #data[1] + fields = {} + for i = 1,ncol do append(fields,'$'..i) end + fields = concat(fields,',') + end + end + local idpat = patterns.IDEN + if numfields then + idpat = '%$(%d+)' + else + -- massage field names to replace non-identifier chars + fields = rstrip(fields):gsub('[^,%w]','_') + end + local massage_fields = utils.bind1(massage_fields,data) + local ret = gsub(fields,idpat,massage_fields) + if field_error then return nil,field_error end + parms.fields = fields + parms.proc_fields = ret + parms.where = parms.where or 'true' + if is_string(parms.where) then + parms.where = gsub(parms.where,idpat,massage_fields) + field_error = nil + end + return true +end + + +parse_select = function(s,data) + local endp + local parms = {} + local w1,w2 = s:find('where ') + local s1,s2 = s:find('sort by ') + if w1 then -- where clause! + endp = (s1 or 0)-1 + parms.where = s:sub(w2+1,endp) + end + if s1 then -- sort by clause (must be last!) + parms.sort_by = s:sub(s2+1) + end + endp = (w1 or s1 or 0)-1 + parms.fields = s:sub(1,endp) + local status,err = process_select(data,parms) + if not status then return nil,err + else return parms end +end + +--- create a query iterator from a select string. +-- Select string has this format:
+-- FIELDLIST [ where LUA-CONDN [ sort by FIELD] ]
+-- FIELDLIST is a comma-separated list of valid fields, or '*'.

+-- The condition can also be a table, with fields 'fields' (comma-sep string or +-- table), 'sort_by' (string) and 'where' (Lua expression string or function) +-- @param data table produced by read +-- @param condn select string or table +-- @param context a list of tables to be searched when resolving functions +-- @param return_row if true, wrap the results in a row table +-- @return an iterator over the specified fields, or nil +-- @return an error message +function data.query(data,condn,context,return_row) + local err + if is_string(condn) then + condn,err = parse_select(condn,data) + if not condn then return nil,err end + elseif type(condn) == 'table' then + if type(condn.fields) == 'table' then + condn.fields = concat(condn.fields,',') + end + if not condn.proc_fields then + local status,err = process_select(data,condn) + if not status then return nil,err end + end + else + return nil, "condition must be a string or a table" + end + local query + if condn.sort_by then -- use sorted_query + query = sorted_query + else + query = simple_query + end + local fields = condn.proc_fields or condn.fields + if return_row then + fields = '{'..fields..'}' + end + query = query:gsub('FIELDLIST',fields) + if is_string(condn.where) then + query = query:gsub('CONDITION',condn.where) + condn.where = nil + else + query = query:gsub('CONDITION','_condn(v)') + condn.where = function_arg(0,condn.where,'condition.where must be callable') + end + if condn.sort_by then + local expr,sort_var,sort_dir + local sort_by = condn.sort_by + local i1,i2 = sort_by:find('%s+') + if i1 then + sort_var,sort_dir = sort_by:sub(1,i1-1),sort_by:sub(i2+1) + else + sort_var = sort_by + sort_dir = 'asc' + end + if sort_var:match '^%$' then sort_var = sort_var:sub(2) end + sort_var = massage_fields(data,sort_var) + if field_error then return nil,field_error end + if sort_dir == 'asc' then + sort_dir = '<' + else + sort_dir = '>' + end + expr = ('%s %s %s'):format(sort_var:gsub('v','v1'),sort_dir,sort_var:gsub('v','v2')) + query = query:gsub('SORT_EXPR',expr) + end + if condn.where then + query = 'return function(_condn) '..query..' end' + end + if _DEBUG then print(query) end + + local fn,err = utils.load(query,'tmp') + if not fn then return nil,err end + fn = fn() -- get the function + if condn.where then + fn = fn(condn.where) + end + local qfun = fn(data) + if context then + -- [specifying context for condition] @context is a list of tables which are + -- 'injected'into the condition's custom context + append(context,_G) + local lookup = {} + utils.setfenv(qfun,lookup) + setmetatable(lookup,{ + __index = function(tbl,key) + -- _G.print(tbl,key) + for k,t in ipairs(context) do + if t[key] then return t[key] end + end + end + }) + end + return qfun +end + + +DataMT.select = data.query +DataMT.select_row = function(d,condn,context) + return data.query(d,condn,context,true) +end + +--- Filter input using a query. +-- @string Q a query string +-- @param infile filename or file-like object +-- @param outfile filename or file-like object +-- @bool dont_fail true if you want to return an error, not just fail +function data.filter (Q,infile,outfile,dont_fail) + local d = data.read(infile or 'stdin') + local out = open_file(outfile or 'stdout') + local iter,err = d:select(Q) + local delim = d.delim + if not iter then + err = 'error: '..err + if dont_fail then + return nil,err + else + utils.quit(1,err) + end + end + while true do + local res = {iter()} + if #res == 0 then break end + out:write(concat(res,delim),'\n') + end +end + +return data + diff --git a/framework/lualib/thirdparty/pl/dir.lua b/framework/lualib/thirdparty/pl/dir.lua new file mode 100755 index 0000000..b19c8fc --- /dev/null +++ b/framework/lualib/thirdparty/pl/dir.lua @@ -0,0 +1,524 @@ +--- Listing files in directories and creating/removing directory paths. +-- +-- Dependencies: `pl.utils`, `pl.path` +-- +-- Soft Dependencies: `alien`, `ffi` (either are used on Windows for copying/moving files) +-- @module pl.dir + +local utils = require 'pl.utils' +local path = require 'pl.path' +local is_windows = path.is_windows +local ldir = path.dir +local mkdir = path.mkdir +local rmdir = path.rmdir +local sub = string.sub +local os,pcall,ipairs,pairs,require,setmetatable = os,pcall,ipairs,pairs,require,setmetatable +local remove = os.remove +local append = table.insert +local assert_arg,assert_string,raise = utils.assert_arg,utils.assert_string,utils.raise + +local exists, isdir = path.exists, path.isdir +local sep = path.sep + +local dir = {} + +local function makelist(l) + return setmetatable(l, require('pl.List')) +end + +local function assert_dir (n,val) + assert_arg(n,val,'string',path.isdir,'not a directory',4) +end + +local function filemask(mask) + mask = utils.escape(path.normcase(mask)) + return '^'..mask:gsub('%%%*','.*'):gsub('%%%?','.')..'$' +end + +--- Test whether a file name matches a shell pattern. +-- Both parameters are case-normalized if operating system is +-- case-insensitive. +-- @string filename A file name. +-- @string pattern A shell pattern. The only special characters are +-- `'*'` and `'?'`: `'*'` matches any sequence of characters and +-- `'?'` matches any single character. +-- @treturn bool +-- @raise dir and mask must be strings +function dir.fnmatch(filename,pattern) + assert_string(1,filename) + assert_string(2,pattern) + return path.normcase(filename):find(filemask(pattern)) ~= nil +end + +--- Return a list of all file names within an array which match a pattern. +-- @tab filenames An array containing file names. +-- @string pattern A shell pattern. +-- @treturn List(string) List of matching file names. +-- @raise dir and mask must be strings +function dir.filter(filenames,pattern) + assert_arg(1,filenames,'table') + assert_string(2,pattern) + local res = {} + local mask = filemask(pattern) + for i,f in ipairs(filenames) do + if path.normcase(f):find(mask) then append(res,f) end + end + return makelist(res) +end + +local function _listfiles(dirname,filemode,match) + local res = {} + local check = utils.choose(filemode,path.isfile,path.isdir) + if not dirname then dirname = '.' end + for f in ldir(dirname) do + if f ~= '.' and f ~= '..' then + local p = path.join(dirname,f) + if check(p) and (not match or match(f)) then + append(res,p) + end + end + end + return makelist(res) +end + +--- return a list of all files in a directory which match a shell pattern. +-- @string dirname A directory. If not given, all files in current directory are returned. +-- @string mask A shell pattern. If not given, all files are returned. +-- @treturn {string} list of files +-- @raise dirname and mask must be strings +function dir.getfiles(dirname,mask) + assert_dir(1,dirname) + if mask then assert_string(2,mask) end + local match + if mask then + mask = filemask(mask) + match = function(f) + return path.normcase(f):find(mask) + end + end + return _listfiles(dirname,true,match) +end + +--- return a list of all subdirectories of the directory. +-- @string dirname A directory +-- @treturn {string} a list of directories +-- @raise dir must be a a valid directory +function dir.getdirectories(dirname) + assert_dir(1,dirname) + return _listfiles(dirname,false) +end + +local alien,ffi,ffi_checked,CopyFile,MoveFile,GetLastError,win32_errors,cmd_tmpfile + +local function execute_command(cmd,parms) + if not cmd_tmpfile then cmd_tmpfile = path.tmpname () end + local err = path.is_windows and ' > ' or ' 2> ' + cmd = cmd..' '..parms..err..utils.quote_arg(cmd_tmpfile) + local ret = utils.execute(cmd) + if not ret then + local err = (utils.readfile(cmd_tmpfile):gsub('\n(.*)','')) + remove(cmd_tmpfile) + return false,err + else + remove(cmd_tmpfile) + return true + end +end + +local function find_ffi_copyfile () + if not ffi_checked then + ffi_checked = true + local res + res,alien = pcall(require,'alien') + if not res then + alien = nil + res, ffi = pcall(require,'ffi') + end + if not res then + ffi = nil + return + end + else + return + end + if alien then + -- register the Win32 CopyFile and MoveFile functions + local kernel = alien.load('kernel32.dll') + CopyFile = kernel.CopyFileA + CopyFile:types{'string','string','int',ret='int',abi='stdcall'} + MoveFile = kernel.MoveFileA + MoveFile:types{'string','string',ret='int',abi='stdcall'} + GetLastError = kernel.GetLastError + GetLastError:types{ret ='int', abi='stdcall'} + elseif ffi then + ffi.cdef [[ + int CopyFileA(const char *src, const char *dest, int iovr); + int MoveFileA(const char *src, const char *dest); + int GetLastError(); + ]] + CopyFile = ffi.C.CopyFileA + MoveFile = ffi.C.MoveFileA + GetLastError = ffi.C.GetLastError + end + win32_errors = { + ERROR_FILE_NOT_FOUND = 2, + ERROR_PATH_NOT_FOUND = 3, + ERROR_ACCESS_DENIED = 5, + ERROR_WRITE_PROTECT = 19, + ERROR_BAD_UNIT = 20, + ERROR_NOT_READY = 21, + ERROR_WRITE_FAULT = 29, + ERROR_READ_FAULT = 30, + ERROR_SHARING_VIOLATION = 32, + ERROR_LOCK_VIOLATION = 33, + ERROR_HANDLE_DISK_FULL = 39, + ERROR_BAD_NETPATH = 53, + ERROR_NETWORK_BUSY = 54, + ERROR_DEV_NOT_EXIST = 55, + ERROR_FILE_EXISTS = 80, + ERROR_OPEN_FAILED = 110, + ERROR_INVALID_NAME = 123, + ERROR_BAD_PATHNAME = 161, + ERROR_ALREADY_EXISTS = 183, + } +end + +local function two_arguments (f1,f2) + return utils.quote_arg(f1)..' '..utils.quote_arg(f2) +end + +local function file_op (is_copy,src,dest,flag) + if flag == 1 and path.exists(dest) then + return false,"cannot overwrite destination" + end + if is_windows then + -- if we haven't tried to load Alien/LuaJIT FFI before, then do so + find_ffi_copyfile() + -- fallback if there's no Alien, just use DOS commands *shudder* + -- 'rename' involves a copy and then deleting the source. + if not CopyFile then + if path.is_windows then + src = src:gsub("/","\\") + dest = dest:gsub("/","\\") + end + local res, err = execute_command('copy',two_arguments(src,dest)) + if not res then return false,err end + if not is_copy then + return execute_command('del',utils.quote_arg(src)) + end + return true + else + if path.isdir(dest) then + dest = path.join(dest,path.basename(src)) + end + local ret + if is_copy then ret = CopyFile(src,dest,flag) + else ret = MoveFile(src,dest) end + if ret == 0 then + local err = GetLastError() + for name,value in pairs(win32_errors) do + if value == err then return false,name end + end + return false,"Error #"..err + else return true + end + end + else -- for Unix, just use cp for now + return execute_command(is_copy and 'cp' or 'mv', + two_arguments(src,dest)) + end +end + +--- copy a file. +-- @string src source file +-- @string dest destination file or directory +-- @bool flag true if you want to force the copy (default) +-- @treturn bool operation succeeded +-- @raise src and dest must be strings +function dir.copyfile (src,dest,flag) + assert_string(1,src) + assert_string(2,dest) + flag = flag==nil or flag + return file_op(true,src,dest,flag and 0 or 1) +end + +--- move a file. +-- @string src source file +-- @string dest destination file or directory +-- @treturn bool operation succeeded +-- @raise src and dest must be strings +function dir.movefile (src,dest) + assert_string(1,src) + assert_string(2,dest) + return file_op(false,src,dest,0) +end + +local function _dirfiles(dirname,attrib) + local dirs = {} + local files = {} + for f in ldir(dirname) do + if f ~= '.' and f ~= '..' then + local p = path.join(dirname,f) + local mode = attrib(p,'mode') + if mode=='directory' then + append(dirs,f) + else + append(files,f) + end + end + end + return makelist(dirs), makelist(files) +end + + +--- return an iterator which walks through a directory tree starting at root. +-- The iterator returns (root,dirs,files) +-- Note that dirs and files are lists of names (i.e. you must say path.join(root,d) +-- to get the actual full path) +-- If bottom_up is false (or not present), then the entries at the current level are returned +-- before we go deeper. This means that you can modify the returned list of directories before +-- continuing. +-- This is a clone of os.walk from the Python libraries. +-- @string root A starting directory +-- @bool bottom_up False if we start listing entries immediately. +-- @bool follow_links follow symbolic links +-- @return an iterator returning root,dirs,files +-- @raise root must be a directory +function dir.walk(root,bottom_up,follow_links) + assert_dir(1,root) + local attrib + if path.is_windows or not follow_links then + attrib = path.attrib + else + attrib = path.link_attrib + end + + local to_scan = { root } + local to_return = {} + local iter = function() + while #to_scan > 0 do + local current_root = table.remove(to_scan) + local dirs,files = _dirfiles(current_root, attrib) + for _, d in ipairs(dirs) do + table.insert(to_scan, current_root..path.sep..d) + end + if not bottom_up then + return current_root, dirs, files + else + table.insert(to_return, { current_root, dirs, files }) + end + end + if #to_return > 0 then + return utils.unpack(table.remove(to_return)) + end + end + + return iter +end + +--- remove a whole directory tree. +-- Symlinks in the tree will be deleted without following them. +-- @string fullpath A directory path (must be an actual directory, not a symlink) +-- @return true or nil +-- @return error if failed +-- @raise fullpath must be a string +function dir.rmtree(fullpath) + assert_dir(1,fullpath) + if path.islink(fullpath) then return false,'will not follow symlink' end + for root,dirs,files in dir.walk(fullpath,true) do + if path.islink(root) then + -- sub dir is a link, remove link, do not follow + if is_windows then + -- Windows requires using "rmdir". Deleting the link like a file + -- will instead delete all files from the target directory!! + local res, err = rmdir(root) + if not res then return nil,err .. ": " .. root end + else + local res, err = remove(root) + if not res then return nil,err .. ": " .. root end + end + else + for i,f in ipairs(files) do + local res, err = remove(path.join(root,f)) + if not res then return nil,err .. ": " .. path.join(root,f) end + end + local res, err = rmdir(root) + if not res then return nil,err .. ": " .. root end + end + end + return true +end + + +do + local dirpat + if path.is_windows then + dirpat = '(.+)\\[^\\]+$' + else + dirpat = '(.+)/[^/]+$' + end + + local _makepath + function _makepath(p) + -- windows root drive case + if p:find '^%a:[\\]*$' then + return true + end + if not path.isdir(p) then + local subp = p:match(dirpat) + if subp then + local ok, err = _makepath(subp) + if not ok then return nil, err end + end + return mkdir(p) + else + return true + end + end + + --- create a directory path. + -- This will create subdirectories as necessary! + -- @string p A directory path + -- @return true on success, nil + errormsg on failure + -- @raise failure to create + function dir.makepath (p) + assert_string(1,p) + if path.is_windows then + p = p:gsub("/", "\\") + end + return _makepath(path.abspath(p)) + end +end + +--- clone a directory tree. Will always try to create a new directory structure +-- if necessary. +-- @string path1 the base path of the source tree +-- @string path2 the new base path for the destination +-- @func file_fun an optional function to apply on all files +-- @bool verbose an optional boolean to control the verbosity of the output. +-- It can also be a logging function that behaves like print() +-- @return true, or nil +-- @return error message, or list of failed directory creations +-- @return list of failed file operations +-- @raise path1 and path2 must be strings +-- @usage clonetree('.','../backup',copyfile) +function dir.clonetree (path1,path2,file_fun,verbose) + assert_string(1,path1) + assert_string(2,path2) + if verbose == true then verbose = print end + local abspath,normcase,isdir,join = path.abspath,path.normcase,path.isdir,path.join + local faildirs,failfiles = {},{} + if not isdir(path1) then return raise 'source is not a valid directory' end + path1 = abspath(normcase(path1)) + path2 = abspath(normcase(path2)) + if verbose then verbose('normalized:',path1,path2) end + -- particularly NB that the new path isn't fully contained in the old path + if path1 == path2 then return raise "paths are the same" end + local _,i2 = path2:find(path1,1,true) + if i2 == #path1 and path2:sub(i2+1,i2+1) == path.sep then + return raise 'destination is a subdirectory of the source' + end + local cp = path.common_prefix (path1,path2) + local idx = #cp + if idx == 0 then -- no common path, but watch out for Windows paths! + if path1:sub(2,2) == ':' then idx = 3 end + end + for root,dirs,files in dir.walk(path1) do + local opath = path2..root:sub(idx) + if verbose then verbose('paths:',opath,root) end + if not isdir(opath) then + local ret = dir.makepath(opath) + if not ret then append(faildirs,opath) end + if verbose then verbose('creating:',opath,ret) end + end + if file_fun then + for i,f in ipairs(files) do + local p1 = join(root,f) + local p2 = join(opath,f) + local ret = file_fun(p1,p2) + if not ret then append(failfiles,p2) end + if verbose then + verbose('files:',p1,p2,ret) + end + end + end + end + return true,faildirs,failfiles +end + + +-- each entry of the stack is an array with three items: +-- 1. the name of the directory +-- 2. the lfs iterator function +-- 3. the lfs iterator userdata +local function treeiter(iterstack) + local diriter = iterstack[#iterstack] + if not diriter then + return -- done + end + + local dirname = diriter[1] + local entry = diriter[2](diriter[3]) + if not entry then + table.remove(iterstack) + return treeiter(iterstack) -- tail-call to try next + end + + if entry ~= "." and entry ~= ".." then + entry = dirname .. sep .. entry + if exists(entry) then -- Just in case a symlink is broken. + local is_dir = isdir(entry) + if is_dir then + table.insert(iterstack, { entry, ldir(entry) }) + end + return entry, is_dir + end + end + + return treeiter(iterstack) -- tail-call to try next +end + + +--- return an iterator over all entries in a directory tree +-- @string d a directory +-- @return an iterator giving pathname and mode (true for dir, false otherwise) +-- @raise d must be a non-empty string +function dir.dirtree( d ) + assert( d and d ~= "", "directory parameter is missing or empty" ) + + local last = sub ( d, -1 ) + if last == sep or last == '/' then + d = sub( d, 1, -2 ) + end + + local iterstack = { {d, ldir(d)} } + + return treeiter, iterstack +end + + +--- Recursively returns all the file starting at _path_. It can optionally take a shell pattern and +-- only returns files that match _shell_pattern_. If a pattern is given it will do a case insensitive search. +-- @string start_path A directory. If not given, all files in current directory are returned. +-- @string shell_pattern A shell pattern. If not given, all files are returned. +-- @treturn List(string) containing all the files found recursively starting at _path_ and filtered by _shell_pattern_. +-- @raise start_path must be a directory +function dir.getallfiles( start_path, shell_pattern ) + assert_dir(1,start_path) + shell_pattern = shell_pattern or "*" + + local files = {} + local normcase = path.normcase + for filename, mode in dir.dirtree( start_path ) do + if not mode then + local mask = filemask( shell_pattern ) + if normcase(filename):find( mask ) then + files[#files + 1] = filename + end + end + end + + return makelist(files) +end + +return dir diff --git a/framework/lualib/thirdparty/pl/file.lua b/framework/lualib/thirdparty/pl/file.lua new file mode 100755 index 0000000..b8058c4 --- /dev/null +++ b/framework/lualib/thirdparty/pl/file.lua @@ -0,0 +1,55 @@ +--- File manipulation functions: reading, writing, moving and copying. +-- +-- This module wraps a number of functions from other modules into a +-- file related module for convenience. +-- +-- Dependencies: `pl.utils`, `pl.dir`, `pl.path` +-- @module pl.file +local os = os +local utils = require 'pl.utils' +local dir = require 'pl.dir' +local path = require 'pl.path' + +local file = {} + +--- return the contents of a file as a string. +-- This function is a copy of `utils.readfile`. +-- @function file.read +file.read = utils.readfile + +--- write a string to a file. +-- This function is a copy of `utils.writefile`. +-- @function file.write +file.write = utils.writefile + +--- copy a file. +-- This function is a copy of `dir.copyfile`. +-- @function file.copy +file.copy = dir.copyfile + +--- move a file. +-- This function is a copy of `dir.movefile`. +-- @function file.move +file.move = dir.movefile + +--- Return the time of last access as the number of seconds since the epoch. +-- This function is a copy of `path.getatime`. +-- @function file.access_time +file.access_time = path.getatime + +---Return when the file was created. +-- This function is a copy of `path.getctime`. +-- @function file.creation_time +file.creation_time = path.getctime + +--- Return the time of last modification. +-- This function is a copy of `path.getmtime`. +-- @function file.modified_time +file.modified_time = path.getmtime + +--- Delete a file. +-- This function is a copy of `os.remove`. +-- @function file.delete +file.delete = os.remove + +return file diff --git a/framework/lualib/thirdparty/pl/func.lua b/framework/lualib/thirdparty/pl/func.lua new file mode 100755 index 0000000..daac0fc --- /dev/null +++ b/framework/lualib/thirdparty/pl/func.lua @@ -0,0 +1,393 @@ +--- Functional helpers like composition, binding and placeholder expressions. +-- Placeholder expressions are useful for short anonymous functions, and were +-- inspired by the Boost Lambda library. +-- +-- > utils.import 'pl.func' +-- > ls = List{10,20,30} +-- > = ls:map(_1+1) +-- {11,21,31} +-- +-- They can also be used to _bind_ particular arguments of a function. +-- +-- > p = bind(print,'start>',_0) +-- > p(10,20,30) +-- > start> 10 20 30 +-- +-- See @{07-functional.md.Creating_Functions_from_Functions|the Guide} +-- +-- Dependencies: `pl.utils`, `pl.tablex` +-- @module pl.func +local type,setmetatable,getmetatable,rawset = type,setmetatable,getmetatable,rawset +local concat,append = table.concat,table.insert +local tostring = tostring +local utils = require 'pl.utils' +local pairs,rawget,unpack,pack = pairs,rawget,utils.unpack,utils.pack +local tablex = require 'pl.tablex' +local map = tablex.map +local _DEBUG = rawget(_G,'_DEBUG') +local assert_arg = utils.assert_arg + +local func = {} + +-- metatable for Placeholder Expressions (PE) +local _PEMT = {} + +local function P (t) + setmetatable(t,_PEMT) + return t +end + +func.PE = P + +local function isPE (obj) + return getmetatable(obj) == _PEMT +end + +func.isPE = isPE + +-- construct a placeholder variable (e.g _1 and _2) +local function PH (idx) + return P {op='X',repr='_'..idx, index=idx} +end + +-- construct a constant placeholder variable (e.g _C1 and _C2) +local function CPH (idx) + return P {op='X',repr='_C'..idx, index=idx} +end + +func._1,func._2,func._3,func._4,func._5 = PH(1),PH(2),PH(3),PH(4),PH(5) +func._0 = P{op='X',repr='...',index=0} + +function func.Var (name) + local ls = utils.split(name,'[%s,]+') + local res = {} + for i = 1, #ls do + append(res,P{op='X',repr=ls[i],index=0}) + end + return unpack(res) +end + +function func._ (value) + return P{op='X',repr=value,index='wrap'} +end + +local repr + +func.Nil = func.Var 'nil' + +function _PEMT.__index(obj,key) + return P{op='[]',obj,key} +end + +function _PEMT.__call(fun,...) + return P{op='()',fun,...} +end + +function _PEMT.__tostring (e) + return repr(e) +end + +function _PEMT.__unm(arg) + return P{op='unm',arg} +end + +function func.Not (arg) + return P{op='not',arg} +end + +function func.Len (arg) + return P{op='#',arg} +end + + +local function binreg(context,t) + for name,op in pairs(t) do + rawset(context,name,function(x,y) + return P{op=op,x,y} + end) + end +end + +local function import_name (name,fun,context) + rawset(context,name,function(...) + return P{op='()',fun,...} + end) +end + +local imported_functions = {} + +local function is_global_table (n) + return type(_G[n]) == 'table' +end + +--- wrap a table of functions. This makes them available for use in +-- placeholder expressions. +-- @string tname a table name +-- @tab context context to put results, defaults to environment of caller +function func.import(tname,context) + assert_arg(1,tname,'string',is_global_table,'arg# 1: not a name of a global table') + local t = _G[tname] + context = context or _G + for name,fun in pairs(t) do + import_name(name,fun,context) + imported_functions[fun] = name + end +end + +--- register a function for use in placeholder expressions. +-- @func fun a function +-- @string[opt] name an optional name +-- @return a placeholder functiond +function func.register (fun,name) + assert_arg(1,fun,'function') + if name then + assert_arg(2,name,'string') + imported_functions[fun] = name + end + return function(...) + return P{op='()',fun,...} + end +end + +function func.lookup_imported_name (fun) + return imported_functions[fun] +end + +local function _arg(...) return ... end + +function func.Args (...) + return P{op='()',_arg,...} +end + +-- binary operators with their precedences (see Lua manual) +-- precedences might be incremented by one before use depending on +-- left- or right-associativity, space them out +local binary_operators = { + ['or'] = 0, + ['and'] = 2, + ['=='] = 4, ['~='] = 4, ['<'] = 4, ['>'] = 4, ['<='] = 4, ['>='] = 4, + ['..'] = 6, + ['+'] = 8, ['-'] = 8, + ['*'] = 10, ['/'] = 10, ['%'] = 10, + ['^'] = 14 +} + +-- unary operators with their precedences +local unary_operators = { + ['not'] = 12, ['#'] = 12, ['unm'] = 12 +} + +-- comparisons (as prefix functions) +binreg (func,{And='and',Or='or',Eq='==',Lt='<',Gt='>',Le='<=',Ge='>='}) + +-- standard binary operators (as metamethods) +binreg (_PEMT,{__add='+',__sub='-',__mul='*',__div='/',__mod='%',__pow='^',__concat='..'}) + +binreg (_PEMT,{__eq='=='}) + +--- all elements of a table except the first. +-- @tab ls a list-like table. +function func.tail (ls) + assert_arg(1,ls,'table') + local res = {} + for i = 2,#ls do + append(res,ls[i]) + end + return res +end + +--- create a string representation of a placeholder expression. +-- @param e a placeholder expression +-- @param lastpred not used +function repr (e,lastpred) + local tail = func.tail + if isPE(e) then + local pred = binary_operators[e.op] or unary_operators[e.op] + if pred then + -- binary or unary operator + local s + if binary_operators[e.op] then + local left_pred = pred + local right_pred = pred + if e.op == '..' or e.op == '^' then + left_pred = left_pred + 1 + else + right_pred = right_pred + 1 + end + local left_arg = repr(e[1], left_pred) + local right_arg = repr(e[2], right_pred) + s = left_arg..' '..e.op..' '..right_arg + else + local op = e.op == 'unm' and '-' or e.op + s = op..' '..repr(e[1], pred) + end + if lastpred and lastpred > pred then + s = '('..s..')' + end + return s + else -- either postfix, or a placeholder + local ls = map(repr,e) + if e.op == '[]' then + return ls[1]..'['..ls[2]..']' + elseif e.op == '()' then + local fn + if ls[1] ~= nil then -- was _args, undeclared! + fn = ls[1] + else + fn = '' + end + return fn..'('..concat(tail(ls),',')..')' + else + return e.repr + end + end + elseif type(e) == 'string' then + return '"'..e..'"' + elseif type(e) == 'function' then + local name = func.lookup_imported_name(e) + if name then return name else return tostring(e) end + else + return tostring(e) --should not really get here! + end +end +func.repr = repr + +-- collect all the non-PE values in this PE into vlist, and replace each occurence +-- with a constant PH (_C1, etc). Return the maximum placeholder index found. +local collect_values +function collect_values (e,vlist) + if isPE(e) then + if e.op ~= 'X' then + local m = 0 + for i = 1,#e do + local subx = e[i] + local pe = isPE(subx) + if pe then + if subx.op == 'X' and subx.index == 'wrap' then + subx = subx.repr + pe = false + else + m = math.max(m,collect_values(subx,vlist)) + end + end + if not pe then + append(vlist,subx) + e[i] = CPH(#vlist) + end + end + return m + else -- was a placeholder, it has an index... + return e.index + end + else -- plain value has no placeholder dependence + return 0 + end +end +func.collect_values = collect_values + +--- instantiate a PE into an actual function. First we find the largest placeholder used, +-- e.g. _2; from this a list of the formal parameters can be build. Then we collect and replace +-- any non-PE values from the PE, and build up a constant binding list. +-- Finally, the expression can be compiled, and e.__PE_function is set. +-- @param e a placeholder expression +-- @return a function +function func.instantiate (e) + local consts,values,parms = {},{},{} + local rep, err, fun + local n = func.collect_values(e,values) + for i = 1,#values do + append(consts,'_C'..i) + if _DEBUG then print(i,values[i]) end + end + for i =1,n do + append(parms,'_'..i) + end + consts = concat(consts,',') + parms = concat(parms,',') + rep = repr(e) + local fstr = ('return function(%s) return function(%s) return %s end end'):format(consts,parms,rep) + if _DEBUG then print(fstr) end + fun,err = utils.load(fstr,'fun') + if not fun then return nil,err end + fun = fun() -- get wrapper + fun = fun(unpack(values)) -- call wrapper (values could be empty) + e.__PE_function = fun + return fun +end + +--- instantiate a PE unless it has already been done. +-- @param e a placeholder expression +-- @return the function +function func.I(e) + if rawget(e,'__PE_function') then + return e.__PE_function + else return func.instantiate(e) + end +end + +utils.add_function_factory(_PEMT,func.I) + +--- bind the first parameter of the function to a value. +-- @function func.bind1 +-- @func fn a function of one or more arguments +-- @param p a value +-- @return a function of one less argument +-- @usage (bind1(math.max,10))(20) == math.max(10,20) +func.bind1 = utils.bind1 +func.curry = func.bind1 + +--- create a function which chains two functions. +-- @func f a function of at least one argument +-- @func g a function of at least one argument +-- @return a function +-- @usage printf = compose(io.write,string.format) +function func.compose (f,g) + return function(...) return f(g(...)) end +end + +--- bind the arguments of a function to given values. +-- `bind(fn,v,_2)` is equivalent to `bind1(fn,v)`. +-- @func fn a function of at least one argument +-- @param ... values or placeholder variables +-- @return a function +-- @usage (bind(f,_1,a))(b) == f(a,b) +-- @usage (bind(f,_2,_1))(a,b) == f(b,a) +function func.bind(fn,...) + local args = pack(...) + local holders,parms,bvalues,values = {},{},{'fn'},{} + local nv,maxplace,varargs = 1,0,false + for i = 1,args.n do + local a = args[i] + if isPE(a) and a.op == 'X' then + append(holders,a.repr) + maxplace = math.max(maxplace,a.index) + if a.index == 0 then varargs = true end + else + local v = '_v'..nv + append(bvalues,v) + append(holders,v) + append(values,a) + nv = nv + 1 + end + end + for np = 1,maxplace do + append(parms,'_'..np) + end + if varargs then append(parms,'...') end + bvalues = concat(bvalues,',') + parms = concat(parms,',') + holders = concat(holders,',') + local fstr = ([[ +return function (%s) + return function(%s) return fn(%s) end +end +]]):format(bvalues,parms,holders) + if _DEBUG then print(fstr) end + local res = utils.load(fstr) + res = res() + return res(fn,unpack(values)) +end + +return func + + diff --git a/framework/lualib/thirdparty/pl/import_into.lua b/framework/lualib/thirdparty/pl/import_into.lua new file mode 100755 index 0000000..6dd2741 --- /dev/null +++ b/framework/lualib/thirdparty/pl/import_into.lua @@ -0,0 +1,91 @@ +-------------- +-- PL loader, for loading all PL libraries, only on demand. +-- Whenever a module is implicitly accesssed, the table will have the module automatically injected. +-- (e.g. `_ENV.tablex`) +-- then that module is dynamically loaded. The submodules are all brought into +-- the table that is provided as the argument, or returned in a new table. +-- If a table is provided, that table's metatable is clobbered, but the values are not. +-- This module returns a single function, which is passed the environment. +-- If this is `true`, then return a 'shadow table' as the module +-- See @{01-introduction.md.To_Inject_or_not_to_Inject_|the Guide} + +-- @module pl.import_into + +return function(env) + local mod + if env == true then + mod = {} + env = {} + end + local env = env or {} + + local modules = { + utils = true,path=true,dir=true,tablex=true,stringio=true,sip=true, + input=true,seq=true,lexer=true,stringx=true, + config=true,pretty=true,data=true,func=true,text=true, + operator=true,lapp=true,array2d=true, + comprehension=true,xml=true,types=true, + test = true, app = true, file = true, class = true, + luabalanced = true, permute = true, template = true, + url = true, compat = true, + -- classes -- + List = true, Map = true, Set = true, + OrderedMap = true, MultiMap = true, Date = true, + } + rawset(env,'utils',require 'pl.utils') + + for name,klass in pairs(env.utils.stdmt) do + klass.__index = function(t,key) + return require ('pl.'..name)[key] + end; + end + + -- ensure that we play nice with libraries that also attach a metatable + -- to the global table; always forward to a custom __index if we don't + -- match + + local _hook,_prev_index + local gmt = {} + local prevenvmt = getmetatable(env) + if prevenvmt then + _prev_index = prevenvmt.__index + if prevenvmt.__newindex then + gmt.__index = prevenvmt.__newindex + end + end + + function gmt.hook(handler) + _hook = handler + end + + function gmt.__index(t,name) + local found = modules[name] + -- either true, or the name of the module containing this class. + -- either way, we load the required module and make it globally available. + if found then + -- e..g pretty.dump causes pl.pretty to become available as 'pretty' + rawset(env,name,require('pl.'..name)) + return env[name] + else + local res + if _hook then + res = _hook(t,name) + if res then return res end + end + if _prev_index then + return _prev_index(t,name) + end + end + end + + if mod then + function gmt.__newindex(t,name,value) + mod[name] = value + rawset(t,name,value) + end + end + + setmetatable(env,gmt) + + return env,mod or env +end diff --git a/framework/lualib/thirdparty/pl/init.lua b/framework/lualib/thirdparty/pl/init.lua new file mode 100755 index 0000000..c27a890 --- /dev/null +++ b/framework/lualib/thirdparty/pl/init.lua @@ -0,0 +1,11 @@ +-------------- +-- Entry point for loading all PL libraries only on demand, into the global space. +-- Requiring 'pl' means that whenever a module is implicitly accesssed +-- (e.g. `utils.split`) +-- then that module is dynamically loaded. The submodules are all brought into +-- the global space. +--Updated to use @{pl.import_into} +-- @module pl +require'pl.import_into'(_G) + +if rawget(_G,'PENLIGHT_STRICT') then require 'pl.strict' end diff --git a/framework/lualib/thirdparty/pl/input.lua b/framework/lualib/thirdparty/pl/input.lua new file mode 100755 index 0000000..f81de87 --- /dev/null +++ b/framework/lualib/thirdparty/pl/input.lua @@ -0,0 +1,171 @@ +--- Iterators for extracting words or numbers from an input source. +-- +-- require 'pl' +-- local total,n = seq.sum(input.numbers()) +-- print('average',total/n) +-- +-- _source_ is defined as a string or a file-like object (i.e. has a read() method which returns the next line) +-- +-- See @{06-data.md.Reading_Unstructured_Text_Data|here} +-- +-- Dependencies: `pl.utils` +-- @module pl.input +local strfind = string.find +local strsub = string.sub +local strmatch = string.match +local utils = require 'pl.utils' +local unpack = utils.unpack +local pairs,type,tonumber = pairs,type,tonumber +local patterns = utils.patterns +local io = io + +local input = {} + +--- create an iterator over all tokens. +-- based on allwords from PiL, 7.1 +-- @func getter any function that returns a line of text +-- @string pattern +-- @string[opt] fn Optionally can pass a function to process each token as it's found. +-- @return an iterator +function input.alltokens (getter,pattern,fn) + local line = getter() -- current line + local pos = 1 -- current position in the line + return function () -- iterator function + while line do -- repeat while there are lines + local s, e = strfind(line, pattern, pos) + if s then -- found a word? + pos = e + 1 -- next position is after this token + local res = strsub(line, s, e) -- return the token + if fn then res = fn(res) end + return res + else + line = getter() -- token not found; try next line + pos = 1 -- restart from first position + end + end + return nil -- no more lines: end of traversal + end +end +local alltokens = input.alltokens + +-- question: shd this _split_ a string containing line feeds? + +--- create a function which grabs the next value from a source. If the source is a string, then the getter +-- will return the string and thereafter return nil. If not specified then the source is assumed to be stdin. +-- @param f a string or a file-like object (i.e. has a read() method which returns the next line) +-- @return a getter function +function input.create_getter(f) + if f then + if type(f) == 'string' then + local ls = utils.split(f,'\n') + local i,n = 0,#ls + return function() + i = i + 1 + if i > n then return nil end + return ls[i] + end + else + -- anything that supports the read() method! + if not f.read then error('not a file-like object') end + return function() return f:read() end + end + else + return io.read -- i.e. just read from stdin + end +end + +--- generate a sequence of numbers from a source. +-- @param f A source +-- @return An iterator +function input.numbers(f) + return alltokens(input.create_getter(f), + '('..patterns.FLOAT..')',tonumber) +end + +--- generate a sequence of words from a source. +-- @param f A source +-- @return An iterator +function input.words(f) + return alltokens(input.create_getter(f),"%w+") +end + +local function apply_tonumber (no_fail,...) + local args = {...} + for i = 1,#args do + local n = tonumber(args[i]) + if n == nil then + if not no_fail then return nil,args[i] end + else + args[i] = n + end + end + return args +end + +--- parse an input source into fields. +-- By default, will fail if it cannot convert a field to a number. +-- @param ids a list of field indices, or a maximum field index +-- @string delim delimiter to parse fields (default space) +-- @param f a source @see create_getter +-- @tab opts option table, `{no_fail=true}` +-- @return an iterator with the field values +-- @usage for x,y in fields {2,3} do print(x,y) end -- 2nd and 3rd fields from stdin +function input.fields (ids,delim,f,opts) + local sep + local s + local getter = input.create_getter(f) + local no_fail = opts and opts.no_fail + local no_convert = opts and opts.no_convert + if not delim or delim == ' ' then + delim = '%s' + sep = '%s+' + s = '%s*' + else + sep = delim + s = '' + end + local max_id = 0 + if type(ids) == 'table' then + for i,id in pairs(ids) do + if id > max_id then max_id = id end + end + else + max_id = ids + ids = {} + for i = 1,max_id do ids[#ids+1] = i end + end + local pat = '[^'..delim..']*' + local k = 1 + for i = 1,max_id do + if ids[k] == i then + k = k + 1 + s = s..'('..pat..')' + else + s = s..pat + end + if i < max_id then + s = s..sep + end + end + local linecount = 1 + return function() + local line,results,err + repeat + line = getter() + linecount = linecount + 1 + if not line then return nil end + if no_convert then + results = {strmatch(line,s)} + else + results,err = apply_tonumber(no_fail,strmatch(line,s)) + if not results then + utils.quit("line "..(linecount-1)..": cannot convert '"..err.."' to number") + end + end + until #results > 0 + return unpack(results) + end +end + +return input + diff --git a/framework/lualib/thirdparty/pl/lapp.lua b/framework/lualib/thirdparty/pl/lapp.lua new file mode 100755 index 0000000..56d1d9d --- /dev/null +++ b/framework/lualib/thirdparty/pl/lapp.lua @@ -0,0 +1,451 @@ +--- Simple command-line parsing using human-readable specification. +-- Supports GNU-style parameters. +-- +-- lapp = require 'pl.lapp' +-- local args = lapp [[ +-- Does some calculations +-- -o,--offset (default 0.0) Offset to add to scaled number +-- -s,--scale (number) Scaling factor +-- (number) Number to be scaled +-- ]] +-- +-- print(args.offset + args.scale * args.number) +-- +-- Lines beginning with `'-'` are flags; there may be a short and a long name; +-- lines beginning with `''` are arguments. Anything in parens after +-- the flag/argument is either a default, a type name or a range constraint. +-- +-- See @{08-additional.md.Command_line_Programs_with_Lapp|the Guide} +-- +-- Dependencies: `pl.sip` +-- @module pl.lapp + +local status,sip = pcall(require,'pl.sip') +if not status then + sip = require 'sip' +end +local match = sip.match_at_start +local append,tinsert = table.insert,table.insert + +sip.custom_pattern('X','(%a[%w_%-]*)') + +local function lines(s) return s:gmatch('([^\n]*)\n') end +local function lstrip(str) return str:gsub('^%s+','') end +local function strip(str) return lstrip(str):gsub('%s+$','') end +local function at(s,k) return s:sub(k,k) end + +local lapp = {} + +local open_files,parms,aliases,parmlist,usage,script + +lapp.callback = false -- keep Strict happy + +local filetypes = { + stdin = {io.stdin,'file-in'}, stdout = {io.stdout,'file-out'}, + stderr = {io.stderr,'file-out'} +} + +--- controls whether to dump usage on error. +-- Defaults to true +lapp.show_usage_error = true + +--- quit this script immediately. +-- @string msg optional message +-- @bool no_usage suppress 'usage' display +function lapp.quit(msg,no_usage) + if no_usage == 'throw' then + error(msg) + end + if msg then + io.stderr:write(msg..'\n\n') + end + if not no_usage then + io.stderr:write(usage) + end + os.exit(1) +end + +--- print an error to stderr and quit. +-- @string msg a message +-- @bool no_usage suppress 'usage' display +function lapp.error(msg,no_usage) + if not lapp.show_usage_error then + no_usage = true + elseif lapp.show_usage_error == 'throw' then + no_usage = 'throw' + end + lapp.quit(script..': '..msg,no_usage) +end + +--- open a file. +-- This will quit on error, and keep a list of file objects for later cleanup. +-- @string file filename +-- @string[opt] opt same as second parameter of `io.open` +function lapp.open (file,opt) + local val,err = io.open(file,opt) + if not val then lapp.error(err,true) end + append(open_files,val) + return val +end + +--- quit if the condition is false. +-- @bool condn a condition +-- @string msg message text +function lapp.assert(condn,msg) + if not condn then + lapp.error(msg) + end +end + +local function range_check(x,min,max,parm) + lapp.assert(min <= x and max >= x,parm..' out of range') +end + +local function xtonumber(s) + local val = tonumber(s) + if not val then lapp.error("unable to convert to number: "..s) end + return val +end + +local types = {} + +local builtin_types = {string=true,number=true,['file-in']='file',['file-out']='file',boolean=true} + +local function convert_parameter(ps,val) + if ps.converter then + val = ps.converter(val) + end + if ps.type == 'number' then + val = xtonumber(val) + elseif builtin_types[ps.type] == 'file' then + val = lapp.open(val,(ps.type == 'file-in' and 'r') or 'w' ) + elseif ps.type == 'boolean' then + return val + end + if ps.constraint then + ps.constraint(val) + end + return val +end + +--- add a new type to Lapp. These appear in parens after the value like +-- a range constraint, e.g. ' (integer) Process PID' +-- @string name name of type +-- @param converter either a function to convert values, or a Lua type name. +-- @func[opt] constraint optional function to verify values, should use lapp.error +-- if failed. +function lapp.add_type (name,converter,constraint) + types[name] = {converter=converter,constraint=constraint} +end + +local function force_short(short) + lapp.assert(#short==1,short..": short parameters should be one character") +end + +-- deducing type of variable from default value; +local function process_default (sval,vtype) + local val, success + if not vtype or vtype == 'number' then + val = tonumber(sval) + end + if val then -- we have a number! + return val,'number' + elseif filetypes[sval] then + local ft = filetypes[sval] + return ft[1],ft[2] + else + if sval == 'true' and not vtype then + return true, 'boolean' + end + if sval:match '^["\']' then sval = sval:sub(2,-2) end + + local ps = types[vtype] or {} + ps.type = vtype + + local show_usage_error = lapp.show_usage_error + lapp.show_usage_error = "throw" + success, val = pcall(convert_parameter, ps, sval) + lapp.show_usage_error = show_usage_error + if success then + return val, vtype or 'string' + end + + return sval,vtype or 'string' + end +end + +--- process a Lapp options string. +-- Usually called as `lapp()`. +-- @string str the options text +-- @tparam {string} args a table of arguments (default is `_G.arg`) +-- @return a table with parameter-value pairs +function lapp.process_options_string(str,args) + local results = {} + local varargs + local arg = args or _G.arg + open_files = {} + parms = {} + aliases = {} + parmlist = {} + + local function check_varargs(s) + local res,cnt = s:gsub('^%.%.%.%s*','') + return res, (cnt > 0) + end + + local function set_result(ps,parm,val) + parm = type(parm) == "string" and parm:gsub("%W", "_") or parm -- so foo-bar becomes foo_bar in Lua + if not ps.varargs then + results[parm] = val + else + if not results[parm] then + results[parm] = { val } + else + append(results[parm],val) + end + end + end + + usage = str + + for _,a in ipairs(arg) do + if a == "-h" or a == "--help" then + return lapp.quit() + end + end + + + for line in lines(str) do + local res = {} + local optparm,defval,vtype,constraint,rest + line = lstrip(line) + local function check(str) + return match(str,line,res) + end + + -- flags: either '-', '-,--' or '--' + if check '-$v{short}, --$o{long} $' or check '-$v{short} $' or check '--$o{long} $' then + if res.long then + optparm = res.long:gsub('[^%w%-]','_') -- I'm not sure the $o pattern will let anything else through? + if #res.rest == 1 then optparm = optparm .. res.rest end + if res.short then aliases[res.short] = optparm end + else + optparm = res.short + end + if res.short and not lapp.slack then force_short(res.short) end + res.rest, varargs = check_varargs(res.rest) + elseif check '$<{name} $' then -- is it ? + -- so becomes input_file ... + optparm,rest = res.name:match '([^%.]+)(.*)' + optparm = optparm:gsub('%A','_') + varargs = rest == '...' + append(parmlist,optparm) + end + -- this is not a pure doc line and specifies the flag/parameter type + if res.rest then + line = res.rest + res = {} + local optional + -- do we have ([optional] [] [default ])? + if match('$({def} $',line,res) or match('$({def}',line,res) then + local typespec = strip(res.def) + local ftype, rest = typespec:match('^(%S+)(.*)$') + rest = strip(rest) + if ftype == 'optional' then + ftype, rest = rest:match('^(%S+)(.*)$') + rest = strip(rest) + optional = true + end + local default + if ftype == 'default' then + default = true + if rest == '' then lapp.error("value must follow default") end + else -- a type specification + if match('$f{min}..$f{max}',ftype,res) then + -- a numerical range like 1..10 + local min,max = res.min,res.max + vtype = 'number' + constraint = function(x) + range_check(x,min,max,optparm) + end + elseif not ftype:match '|' then -- plain type + vtype = ftype + else + -- 'enum' type is a string which must belong to + -- one of several distinct values + local enums = ftype + local enump = '|' .. enums .. '|' + vtype = 'string' + constraint = function(s) + lapp.assert(enump:match('|'..s..'|'), + "value '"..s.."' not in "..enums + ) + end + end + end + res.rest = rest + typespec = res.rest + -- optional 'default value' clause. Type is inferred as + -- 'string' or 'number' if there's no explicit type + if default or match('default $r{rest}',typespec,res) then + defval,vtype = process_default(res.rest,vtype) + end + else -- must be a plain flag, no extra parameter required + defval = false + vtype = 'boolean' + end + local ps = { + type = vtype, + defval = defval, + required = defval == nil and not optional, + comment = res.rest or optparm, + constraint = constraint, + varargs = varargs + } + varargs = nil + if types[vtype] then + local converter = types[vtype].converter + if type(converter) == 'string' then + ps.type = converter + else + ps.converter = converter + end + ps.constraint = types[vtype].constraint + elseif not builtin_types[vtype] and vtype then + lapp.error(vtype.." is unknown type") + end + parms[optparm] = ps + end + end + -- cool, we have our parms, let's parse the command line args + local iparm = 1 + local iextra = 1 + local i = 1 + local parm,ps,val + local end_of_flags = false + + local function check_parm (parm) + local eqi = parm:find '[=:]' + if eqi then + tinsert(arg,i+1,parm:sub(eqi+1)) + parm = parm:sub(1,eqi-1) + end + return parm,eqi + end + + local function is_flag (parm) + return parms[aliases[parm] or parm] + end + + while i <= #arg do + local theArg = arg[i] + local res = {} + -- after '--' we don't parse args and they end up in + -- the array part of the result (args[1] etc) + if theArg == '--' then + end_of_flags = true + iparm = #parmlist + 1 + i = i + 1 + theArg = arg[i] + if not theArg then + break + end + end + -- look for a flag, - or -- + if not end_of_flags and (match('--$S{long}',theArg,res) or match('-$S{short}',theArg,res)) then + if res.long then -- long option + parm = check_parm(res.long) + elseif #res.short == 1 or is_flag(res.short) then + parm = res.short + else + local parmstr,eq = check_parm(res.short) + if not eq then + parm = at(parmstr,1) + local flag = is_flag(parm) + if flag and flag.type ~= 'boolean' then + --if isdigit(at(parmstr,2)) then + -- a short option followed by a digit is an exception (for AW;)) + -- push ahead into the arg array + tinsert(arg,i+1,parmstr:sub(2)) + else + -- push multiple flags into the arg array! + for k = 2,#parmstr do + tinsert(arg,i+k-1,'-'..at(parmstr,k)) + end + end + else + parm = parmstr + end + end + if aliases[parm] then parm = aliases[parm] end + if not parms[parm] and (parm == 'h' or parm == 'help') then + lapp.quit() + end + else -- a parameter + parm = parmlist[iparm] + if not parm then + -- extra unnamed parameters are indexed starting at 1 + parm = iextra + ps = { type = 'string' } + parms[parm] = ps + iextra = iextra + 1 + else + ps = parms[parm] + end + if not ps.varargs then + iparm = iparm + 1 + end + val = theArg + end + ps = parms[parm] + if not ps then lapp.error("unrecognized parameter: "..parm) end + if ps.type ~= 'boolean' then -- we need a value! This should follow + if not val then + i = i + 1 + val = arg[i] + theArg = val + end + lapp.assert(val,parm.." was expecting a value") + else -- toggle boolean flags (usually false -> true) + val = not ps.defval + end + ps.used = true + val = convert_parameter(ps,val) + set_result(ps,parm,val) + if builtin_types[ps.type] == 'file' then + set_result(ps,parm..'_name',theArg) + end + if lapp.callback then + lapp.callback(parm,theArg,res) + end + i = i + 1 + val = nil + end + -- check unused parms, set defaults and check if any required parameters were missed + for parm,ps in pairs(parms) do + if not ps.used then + if ps.required then lapp.error("missing required parameter: "..parm) end + set_result(ps,parm,ps.defval) + end + end + return results +end + +if arg then + script = arg[0] + script = script or rawget(_G,"LAPP_SCRIPT") or "unknown" + -- strip dir and extension to get current script name + script = script:gsub('.+[\\/]',''):gsub('%.%a+$','') +else + script = "inter" +end + + +setmetatable(lapp, { + __call = function(tbl,str,args) return lapp.process_options_string(str,args) end, +}) + + +return lapp + + diff --git a/framework/lualib/thirdparty/pl/lexer.lua b/framework/lualib/thirdparty/pl/lexer.lua new file mode 100755 index 0000000..9219716 --- /dev/null +++ b/framework/lualib/thirdparty/pl/lexer.lua @@ -0,0 +1,515 @@ +--- Lexical scanner for creating a sequence of tokens from text. +-- `lexer.scan(s)` returns an iterator over all tokens found in the +-- string `s`. This iterator returns two values, a token type string +-- (such as 'string' for quoted string, 'iden' for identifier) and the value of the +-- token. +-- +-- Versions specialized for Lua and C are available; these also handle block comments +-- and classify keywords as 'keyword' tokens. For example: +-- +-- > s = 'for i=1,n do' +-- > for t,v in lexer.lua(s) do print(t,v) end +-- keyword for +-- iden i +-- = = +-- number 1 +-- , , +-- iden n +-- keyword do +-- +-- See the Guide for further @{06-data.md.Lexical_Scanning|discussion} +-- @module pl.lexer + +local strfind = string.find +local strsub = string.sub +local append = table.insert + + +local function assert_arg(idx,val,tp) + if type(val) ~= tp then + error("argument "..idx.." must be "..tp, 2) + end +end + +local lexer = {} + +local NUMBER1 = '^[%+%-]?%d+%.?%d*[eE][%+%-]?%d+' +local NUMBER1a = '^[%+%-]?%d*%.%d+[eE][%+%-]?%d+' +local NUMBER2 = '^[%+%-]?%d+%.?%d*' +local NUMBER2a = '^[%+%-]?%d*%.%d+' +local NUMBER3 = '^0x[%da-fA-F]+' +local NUMBER4 = '^%d+%.?%d*[eE][%+%-]?%d+' +local NUMBER4a = '^%d*%.%d+[eE][%+%-]?%d+' +local NUMBER5 = '^%d+%.?%d*' +local NUMBER5a = '^%d*%.%d+' +local IDEN = '^[%a_][%w_]*' +local WSPACE = '^%s+' +local STRING1 = "^(['\"])%1" -- empty string +local STRING2 = [[^(['"])(\*)%2%1]] +local STRING3 = [[^(['"]).-[^\](\*)%2%1]] +local CHAR1 = "^''" +local CHAR2 = [[^'(\*)%1']] +local CHAR3 = [[^'.-[^\](\*)%1']] +local PREPRO = '^#.-[^\\]\n' + +local plain_matches,lua_matches,cpp_matches,lua_keyword,cpp_keyword + +local function tdump(tok) + return tok,tok +end + +local function ndump(tok,options) + if options and options.number then + tok = tonumber(tok) + end + return "number",tok +end + +-- regular strings, single or double quotes; usually we want them +-- without the quotes +local function sdump(tok,options) + if options and options.string then + tok = tok:sub(2,-2) + end + return "string",tok +end + +-- long Lua strings need extra work to get rid of the quotes +local function sdump_l(tok,options,findres) + if options and options.string then + local quotelen = 3 + if findres[3] then + quotelen = quotelen + findres[3]:len() + end + tok = tok:sub(quotelen, -quotelen) + if tok:sub(1, 1) == "\n" then + tok = tok:sub(2) + end + end + return "string",tok +end + +local function chdump(tok,options) + if options and options.string then + tok = tok:sub(2,-2) + end + return "char",tok +end + +local function cdump(tok) + return "comment",tok +end + +local function wsdump (tok) + return "space",tok +end + +local function pdump (tok) + return "prepro",tok +end + +local function plain_vdump(tok) + return "iden",tok +end + +local function lua_vdump(tok) + if lua_keyword[tok] then + return "keyword",tok + else + return "iden",tok + end +end + +local function cpp_vdump(tok) + if cpp_keyword[tok] then + return "keyword",tok + else + return "iden",tok + end +end + +--- create a plain token iterator from a string or file-like object. +-- @tparam string|file s a string or a file-like object with `:read()` method returning lines. +-- @tab matches an optional match table - array of token descriptions. +-- A token is described by a `{pattern, action}` pair, where `pattern` should match +-- token body and `action` is a function called when a token of described type is found. +-- @tab[opt] filter a table of token types to exclude, by default `{space=true}` +-- @tab[opt] options a table of options; by default, `{number=true,string=true}`, +-- which means convert numbers and strip string quotes. +function lexer.scan(s,matches,filter,options) + local file = type(s) ~= 'string' and s + filter = filter or {space=true} + options = options or {number=true,string=true} + if filter then + if filter.space then filter[wsdump] = true end + if filter.comments then + filter[cdump] = true + end + end + if not matches then + if not plain_matches then + plain_matches = { + {WSPACE,wsdump}, + {NUMBER3,ndump}, + {IDEN,plain_vdump}, + {NUMBER1,ndump}, + {NUMBER1a,ndump}, + {NUMBER2,ndump}, + {NUMBER2a,ndump}, + {STRING1,sdump}, + {STRING2,sdump}, + {STRING3,sdump}, + {'^.',tdump} + } + end + matches = plain_matches + end + + local line_nr = 0 + local next_line = file and file:read() + local sz = file and 0 or #s + local idx = 1 + + local tlist_i + local tlist + + local first_hit = true + + local function iter(res) + local tp = type(res) + + if tlist then -- returning the inserted token list + local cur = tlist[tlist_i] + if cur then + tlist_i = tlist_i + 1 + return cur[1], cur[2] + else + tlist = nil + end + end + + if tp == 'string' then -- search up to some special pattern + local i1,i2 = strfind(s,res,idx) + if i1 then + local tok = strsub(s,i1,i2) + idx = i2 + 1 + return '', tok + else + idx = sz + 1 + return '', '' + end + + elseif tp == 'table' then -- insert a token list + tlist_i = 1 + tlist = res + return '', '' + + elseif tp ~= 'nil' then -- return position + return line_nr, idx + + else -- look for next token + if first_hit then + if not file then line_nr = 1 end + first_hit = false + end + + if idx > sz then + if file then + if not next_line then + return -- past the end of file, done + end + s = next_line + line_nr = line_nr + 1 + next_line = file:read() + if next_line then + s = s .. '\n' + end + idx, sz = 1, #s + else + return -- past the end of input, done + end + end + + for _,m in ipairs(matches) do + local pat = m[1] + local fun = m[2] + local findres = {strfind(s,pat,idx)} + local i1, i2 = findres[1], findres[2] + if i1 then + local tok = strsub(s,i1,i2) + idx = i2 + 1 + local ret1, ret2 + if not (filter and filter[fun]) then + lexer.finished = idx > sz + ret1, ret2 = fun(tok, options, findres) + end + if not file and tok:find("\n") then + -- Update line number. + local _, newlines = tok:gsub("\n", {}) + line_nr = line_nr + newlines + end + if ret1 then + return ret1, ret2 -- found a match + else + return iter() -- tail-call to try again + end + end + end + end + end + + return iter +end + +local function isstring (s) + return type(s) == 'string' +end + +--- insert tokens into a stream. +-- @param tok a token stream +-- @param a1 a string is the type, a table is a token list and +-- a function is assumed to be a token-like iterator (returns type & value) +-- @string a2 a string is the value +function lexer.insert (tok,a1,a2) + if not a1 then return end + local ts + if isstring(a1) and isstring(a2) then + ts = {{a1,a2}} + elseif type(a1) == 'function' then + ts = {} + for t,v in a1() do + append(ts,{t,v}) + end + else + ts = a1 + end + tok(ts) +end + +--- get everything in a stream upto a newline. +-- @param tok a token stream +-- @return a string +function lexer.getline (tok) + local _,v = tok('.-\n') + return v +end + +--- get current line number. +-- @param tok a token stream +-- @return the line number. +-- if the input source is a file-like object, +-- also return the column. +function lexer.lineno (tok) + return tok(0) +end + +--- get the rest of the stream. +-- @param tok a token stream +-- @return a string +function lexer.getrest (tok) + local _,v = tok('.+') + return v +end + +--- get the Lua keywords as a set-like table. +-- So `res["and"]` etc would be `true`. +-- @return a table +function lexer.get_keywords () + if not lua_keyword then + lua_keyword = { + ["and"] = true, ["break"] = true, ["do"] = true, + ["else"] = true, ["elseif"] = true, ["end"] = true, + ["false"] = true, ["for"] = true, ["function"] = true, + ["if"] = true, ["in"] = true, ["local"] = true, ["nil"] = true, + ["not"] = true, ["or"] = true, ["repeat"] = true, + ["return"] = true, ["then"] = true, ["true"] = true, + ["until"] = true, ["while"] = true + } + end + return lua_keyword +end + +--- create a Lua token iterator from a string or file-like object. +-- Will return the token type and value. +-- @string s the string +-- @tab[opt] filter a table of token types to exclude, by default `{space=true,comments=true}` +-- @tab[opt] options a table of options; by default, `{number=true,string=true}`, +-- which means convert numbers and strip string quotes. +function lexer.lua(s,filter,options) + filter = filter or {space=true,comments=true} + lexer.get_keywords() + if not lua_matches then + lua_matches = { + {WSPACE,wsdump}, + {NUMBER3,ndump}, + {IDEN,lua_vdump}, + {NUMBER4,ndump}, + {NUMBER4a,ndump}, + {NUMBER5,ndump}, + {NUMBER5a,ndump}, + {STRING1,sdump}, + {STRING2,sdump}, + {STRING3,sdump}, + {'^%-%-%[(=*)%[.-%]%1%]',cdump}, + {'^%-%-.-\n',cdump}, + {'^%[(=*)%[.-%]%1%]',sdump_l}, + {'^==',tdump}, + {'^~=',tdump}, + {'^<=',tdump}, + {'^>=',tdump}, + {'^%.%.%.',tdump}, + {'^%.%.',tdump}, + {'^.',tdump} + } + end + return lexer.scan(s,lua_matches,filter,options) +end + +--- create a C/C++ token iterator from a string or file-like object. +-- Will return the token type type and value. +-- @string s the string +-- @tab[opt] filter a table of token types to exclude, by default `{space=true,comments=true}` +-- @tab[opt] options a table of options; by default, `{number=true,string=true}`, +-- which means convert numbers and strip string quotes. +function lexer.cpp(s,filter,options) + filter = filter or {space=true,comments=true} + if not cpp_keyword then + cpp_keyword = { + ["class"] = true, ["break"] = true, ["do"] = true, ["sizeof"] = true, + ["else"] = true, ["continue"] = true, ["struct"] = true, + ["false"] = true, ["for"] = true, ["public"] = true, ["void"] = true, + ["private"] = true, ["protected"] = true, ["goto"] = true, + ["if"] = true, ["static"] = true, ["const"] = true, ["typedef"] = true, + ["enum"] = true, ["char"] = true, ["int"] = true, ["bool"] = true, + ["long"] = true, ["float"] = true, ["true"] = true, ["delete"] = true, + ["double"] = true, ["while"] = true, ["new"] = true, + ["namespace"] = true, ["try"] = true, ["catch"] = true, + ["switch"] = true, ["case"] = true, ["extern"] = true, + ["return"] = true,["default"] = true,['unsigned'] = true,['signed'] = true, + ["union"] = true, ["volatile"] = true, ["register"] = true,["short"] = true, + } + end + if not cpp_matches then + cpp_matches = { + {WSPACE,wsdump}, + {PREPRO,pdump}, + {NUMBER3,ndump}, + {IDEN,cpp_vdump}, + {NUMBER4,ndump}, + {NUMBER4a,ndump}, + {NUMBER5,ndump}, + {NUMBER5a,ndump}, + {CHAR1,chdump}, + {CHAR2,chdump}, + {CHAR3,chdump}, + {STRING1,sdump}, + {STRING2,sdump}, + {STRING3,sdump}, + {'^//.-\n',cdump}, + {'^/%*.-%*/',cdump}, + {'^==',tdump}, + {'^!=',tdump}, + {'^<=',tdump}, + {'^>=',tdump}, + {'^->',tdump}, + {'^&&',tdump}, + {'^||',tdump}, + {'^%+%+',tdump}, + {'^%-%-',tdump}, + {'^%+=',tdump}, + {'^%-=',tdump}, + {'^%*=',tdump}, + {'^/=',tdump}, + {'^|=',tdump}, + {'^%^=',tdump}, + {'^::',tdump}, + {'^.',tdump} + } + end + return lexer.scan(s,cpp_matches,filter,options) +end + +--- get a list of parameters separated by a delimiter from a stream. +-- @param tok the token stream +-- @string[opt=')'] endtoken end of list. Can be '\n' +-- @string[opt=','] delim separator +-- @return a list of token lists. +function lexer.get_separated_list(tok,endtoken,delim) + endtoken = endtoken or ')' + delim = delim or ',' + local parm_values = {} + local level = 1 -- used to count ( and ) + local tl = {} + local function tappend (tl,t,val) + val = val or t + append(tl,{t,val}) + end + local is_end + if endtoken == '\n' then + is_end = function(t,val) + return t == 'space' and val:find '\n' + end + else + is_end = function (t) + return t == endtoken + end + end + local token,value + while true do + token,value=tok() + if not token then return nil,'EOS' end -- end of stream is an error! + if is_end(token,value) and level == 1 then + append(parm_values,tl) + break + elseif token == '(' then + level = level + 1 + tappend(tl,'(') + elseif token == ')' then + level = level - 1 + if level == 0 then -- finished with parm list + append(parm_values,tl) + break + else + tappend(tl,')') + end + elseif token == delim and level == 1 then + append(parm_values,tl) -- a new parm + tl = {} + else + tappend(tl,token,value) + end + end + return parm_values,{token,value} +end + +--- get the next non-space token from the stream. +-- @param tok the token stream. +function lexer.skipws (tok) + local t,v = tok() + while t == 'space' do + t,v = tok() + end + return t,v +end + +local skipws = lexer.skipws + +--- get the next token, which must be of the expected type. +-- Throws an error if this type does not match! +-- @param tok the token stream +-- @string expected_type the token type +-- @bool no_skip_ws whether we should skip whitespace +function lexer.expecting (tok,expected_type,no_skip_ws) + assert_arg(1,tok,'function') + assert_arg(2,expected_type,'string') + local t,v + if no_skip_ws then + t,v = tok() + else + t,v = skipws(tok) + end + if t ~= expected_type then error ("expecting "..expected_type,2) end + return v +end + +return lexer diff --git a/framework/lualib/thirdparty/pl/luabalanced.lua b/framework/lualib/thirdparty/pl/luabalanced.lua new file mode 100755 index 0000000..a1f7dc6 --- /dev/null +++ b/framework/lualib/thirdparty/pl/luabalanced.lua @@ -0,0 +1,264 @@ +--- Extract delimited Lua sequences from strings. +-- Inspired by Damian Conway's Text::Balanced in Perl.
+--
    +--
  • [1] Lua Wiki Page
  • +--
  • [2] http://search.cpan.org/dist/Text-Balanced/lib/Text/Balanced.pm
  • +--

+--
+-- local lb = require "pl.luabalanced"
+-- --Extract Lua expression starting at position 4.
+--  print(lb.match_expression("if x^2 + x > 5 then print(x) end", 4))
+--  --> x^2 + x > 5     16
+-- --Extract Lua string starting at (default) position 1.
+-- print(lb.match_string([["test\"123" .. "more"]]))
+-- --> "test\"123"     12
+-- 
+-- (c) 2008, David Manura, Licensed under the same terms as Lua (MIT license). +-- @class module +-- @name pl.luabalanced + +local M = {} + +local assert = assert + +-- map opening brace <-> closing brace. +local ends = { ['('] = ')', ['{'] = '}', ['['] = ']' } +local begins = {}; for k,v in pairs(ends) do begins[v] = k end + + +-- Match Lua string in string starting at position . +-- Returns , , where is the matched +-- string (or nil on no match) and is the character +-- following the match (or on no match). +-- Supports all Lua string syntax: "...", '...', [[...]], [=[...]=], etc. +local function match_string(s, pos) + pos = pos or 1 + local posa = pos + local c = s:sub(pos,pos) + if c == '"' or c == "'" then + pos = pos + 1 + while 1 do + pos = assert(s:find("[" .. c .. "\\]", pos), 'syntax error') + if s:sub(pos,pos) == c then + local part = s:sub(posa, pos) + return part, pos + 1 + else + pos = pos + 2 + end + end + else + local sc = s:match("^%[(=*)%[", pos) + if sc then + local _; _, pos = s:find("%]" .. sc .. "%]", pos) + assert(pos) + local part = s:sub(posa, pos) + return part, pos + 1 + else + return nil, pos + end + end +end +M.match_string = match_string + + +-- Match bracketed Lua expression, e.g. "(...)", "{...}", "[...]", "[[...]]", +-- [=[...]=], etc. +-- Function interface is similar to match_string. +local function match_bracketed(s, pos) + pos = pos or 1 + local posa = pos + local ca = s:sub(pos,pos) + if not ends[ca] then + return nil, pos + end + local stack = {} + while 1 do + pos = s:find('[%(%{%[%)%}%]\"\']', pos) + assert(pos, 'syntax error: unbalanced') + local c = s:sub(pos,pos) + if c == '"' or c == "'" then + local part; part, pos = match_string(s, pos) + assert(part) + elseif ends[c] then -- open + local mid, posb + if c == '[' then mid, posb = s:match('^%[(=*)%[()', pos) end + if mid then + pos = s:match('%]' .. mid .. '%]()', posb) + assert(pos, 'syntax error: long string not terminated') + if #stack == 0 then + local part = s:sub(posa, pos-1) + return part, pos + end + else + stack[#stack+1] = c + pos = pos + 1 + end + else -- close + assert(stack[#stack] == assert(begins[c]), 'syntax error: unbalanced') + stack[#stack] = nil + if #stack == 0 then + local part = s:sub(posa, pos) + return part, pos+1 + end + pos = pos + 1 + end + end +end +M.match_bracketed = match_bracketed + + +-- Match Lua comment, e.g. "--...\n", "--[[...]]", "--[=[...]=]", etc. +-- Function interface is similar to match_string. +local function match_comment(s, pos) + pos = pos or 1 + if s:sub(pos, pos+1) ~= '--' then + return nil, pos + end + pos = pos + 2 + local partt, post = match_string(s, pos) + if partt then + return '--' .. partt, post + end + local part; part, pos = s:match('^([^\n]*\n?)()', pos) + return '--' .. part, pos +end + + +-- Match Lua expression, e.g. "a + b * c[e]". +-- Function interface is similar to match_string. +local wordop = {['and']=true, ['or']=true, ['not']=true} +local is_compare = {['>']=true, ['<']=true, ['~']=true} +local function match_expression(s, pos) + pos = pos or 1 + local _ + local posa = pos + local lastident + local poscs, posce + while pos do + local c = s:sub(pos,pos) + if c == '"' or c == "'" or c == '[' and s:find('^[=%[]', pos+1) then + local part; part, pos = match_string(s, pos) + assert(part, 'syntax error') + elseif c == '-' and s:sub(pos+1,pos+1) == '-' then + -- note: handle adjacent comments in loop to properly support + -- backtracing (poscs/posce). + poscs = pos + while s:sub(pos,pos+1) == '--' do + local part; part, pos = match_comment(s, pos) + assert(part) + pos = s:match('^%s*()', pos) + posce = pos + end + elseif c == '(' or c == '{' or c == '[' then + _, pos = match_bracketed(s, pos) + elseif c == '=' and s:sub(pos+1,pos+1) == '=' then + pos = pos + 2 -- skip over two-char op containing '=' + elseif c == '=' and is_compare[s:sub(pos-1,pos-1)] then + pos = pos + 1 -- skip over two-char op containing '=' + elseif c:match'^[%)%}%];,=]' then + local part = s:sub(posa, pos-1) + return part, pos + elseif c:match'^[%w_]' then + local newident,newpos = s:match('^([%w_]+)()', pos) + if pos ~= posa and not wordop[newident] then -- non-first ident + local pose = ((posce == pos) and poscs or pos) - 1 + while s:match('^%s', pose) do pose = pose - 1 end + local ce = s:sub(pose,pose) + if ce:match'[%)%}\'\"%]]' or + ce:match'[%w_]' and not wordop[lastident] + then + local part = s:sub(posa, pos-1) + return part, pos + end + end + lastident, pos = newident, newpos + else + pos = pos + 1 + end + pos = s:find('[%(%{%[%)%}%]\"\';,=%w_%-]', pos) + end + local part = s:sub(posa, #s) + return part, #s+1 +end +M.match_expression = match_expression + + +-- Match name list (zero or more names). E.g. "a,b,c" +-- Function interface is similar to match_string, +-- but returns array as match. +local function match_namelist(s, pos) + pos = pos or 1 + local list = {} + while 1 do + local c = #list == 0 and '^' or '^%s*,%s*' + local item, post = s:match(c .. '([%a_][%w_]*)%s*()', pos) + if item then pos = post else break end + list[#list+1] = item + end + return list, pos +end +M.match_namelist = match_namelist + + +-- Match expression list (zero or more expressions). E.g. "a+b,b*c". +-- Function interface is similar to match_string, +-- but returns array as match. +local function match_explist(s, pos) + pos = pos or 1 + local list = {} + while 1 do + if #list ~= 0 then + local post = s:match('^%s*,%s*()', pos) + if post then pos = post else break end + end + local item; item, pos = match_expression(s, pos) + assert(item, 'syntax error') + list[#list+1] = item + end + return list, pos +end +M.match_explist = match_explist + + +-- Replace snippets of code in Lua code string +-- using replacement function f(u,sin) --> sout. +-- is the type of snippet ('c' = comment, 's' = string, +-- 'e' = any other code). +-- Snippet is replaced with (unless is nil or false, in +-- which case the original snippet is kept) +-- This is somewhat analogous to string.gsub . +local function gsub(s, f) + local pos = 1 + local posa = 1 + local sret = '' + while 1 do + pos = s:find('[%-\'\"%[]', pos) + if not pos then break end + if s:match('^%-%-', pos) then + local exp = s:sub(posa, pos-1) + if #exp > 0 then sret = sret .. (f('e', exp) or exp) end + local comment; comment, pos = match_comment(s, pos) + sret = sret .. (f('c', assert(comment)) or comment) + posa = pos + else + local posb = s:find('^[\'\"%[]', pos) + local str + if posb then str, pos = match_string(s, posb) end + if str then + local exp = s:sub(posa, posb-1) + if #exp > 0 then sret = sret .. (f('e', exp) or exp) end + sret = sret .. (f('s', str) or str) + posa = pos + else + pos = pos + 1 + end + end + end + local exp = s:sub(posa) + if #exp > 0 then sret = sret .. (f('e', exp) or exp) end + return sret +end +M.gsub = gsub + + +return M diff --git a/framework/lualib/thirdparty/pl/operator.lua b/framework/lualib/thirdparty/pl/operator.lua new file mode 100755 index 0000000..60eaffd --- /dev/null +++ b/framework/lualib/thirdparty/pl/operator.lua @@ -0,0 +1,209 @@ +--- Lua operators available as functions. +-- +-- (similar to the Python module of the same name) +-- +-- There is a module field `optable` which maps the operator strings +-- onto these functions, e.g. `operator.optable['()']==operator.call` +-- +-- Operator strings like '>' and '{}' can be passed to most Penlight functions +-- expecting a function argument. +-- +-- @module pl.operator + +local strfind = string.find + +local operator = {} + +--- apply function to some arguments **()** +-- @param fn a function or callable object +-- @param ... arguments +function operator.call(fn,...) + return fn(...) +end + +--- get the indexed value from a table **[]** +-- @param t a table or any indexable object +-- @param k the key +function operator.index(t,k) + return t[k] +end + +--- returns true if arguments are equal **==** +-- @param a value +-- @param b value +function operator.eq(a,b) + return a==b +end + +--- returns true if arguments are not equal **~=** + -- @param a value +-- @param b value +function operator.neq(a,b) + return a~=b +end + +--- returns true if a is less than b **<** +-- @param a value +-- @param b value +function operator.lt(a,b) + return a < b +end + +--- returns true if a is less or equal to b **<=** +-- @param a value +-- @param b value +function operator.le(a,b) + return a <= b +end + +--- returns true if a is greater than b **>** +-- @param a value +-- @param b value +function operator.gt(a,b) + return a > b +end + +--- returns true if a is greater or equal to b **>=** +-- @param a value +-- @param b value +function operator.ge(a,b) + return a >= b +end + +--- returns length of string or table **#** +-- @param a a string or a table +function operator.len(a) + return #a +end + +--- add two values **+** +-- @param a value +-- @param b value +function operator.add(a,b) + return a+b +end + +--- subtract b from a **-** +-- @param a value +-- @param b value +function operator.sub(a,b) + return a-b +end + +--- multiply two values __*__ +-- @param a value +-- @param b value +function operator.mul(a,b) + return a*b +end + +--- divide first value by second **/** +-- @param a value +-- @param b value +function operator.div(a,b) + return a/b +end + +--- raise first to the power of second **^** +-- @param a value +-- @param b value +function operator.pow(a,b) + return a^b +end + +--- modulo; remainder of a divided by b **%** +-- @param a value +-- @param b value +function operator.mod(a,b) + return a%b +end + +--- concatenate two values (either strings or `__concat` defined) **..** +-- @param a value +-- @param b value +function operator.concat(a,b) + return a..b +end + +--- return the negative of a value **-** +-- @param a value +function operator.unm(a) + return -a +end + +--- false if value evaluates as true **not** +-- @param a value +function operator.lnot(a) + return not a +end + +--- true if both values evaluate as true **and** +-- @param a value +-- @param b value +function operator.land(a,b) + return a and b +end + +--- true if either value evaluate as true **or** +-- @param a value +-- @param b value +function operator.lor(a,b) + return a or b +end + +--- make a table from the arguments **{}** +-- @param ... non-nil arguments +-- @return a table +function operator.table (...) + return {...} +end + +--- match two strings **~**. +-- uses @{string.find} +function operator.match (a,b) + return strfind(a,b)~=nil +end + +--- the null operation. +-- @param ... arguments +-- @return the arguments +function operator.nop (...) + return ... +end + +---- Map from operator symbol to function. +-- Most of these map directly from operators; +-- But note these extras +-- +-- * __'()'__ `call` +-- * __'[]'__ `index` +-- * __'{}'__ `table` +-- * __'~'__ `match` +-- +-- @table optable +-- @field operator + operator.optable = { + ['+']=operator.add, + ['-']=operator.sub, + ['*']=operator.mul, + ['/']=operator.div, + ['%']=operator.mod, + ['^']=operator.pow, + ['..']=operator.concat, + ['()']=operator.call, + ['[]']=operator.index, + ['<']=operator.lt, + ['<=']=operator.le, + ['>']=operator.gt, + ['>=']=operator.ge, + ['==']=operator.eq, + ['~=']=operator.neq, + ['#']=operator.len, + ['and']=operator.land, + ['or']=operator.lor, + ['{}']=operator.table, + ['~']=operator.match, + ['']=operator.nop, +} + +return operator diff --git a/framework/lualib/thirdparty/pl/path.lua b/framework/lualib/thirdparty/pl/path.lua new file mode 100755 index 0000000..66d83f6 --- /dev/null +++ b/framework/lualib/thirdparty/pl/path.lua @@ -0,0 +1,570 @@ +--- Path manipulation and file queries. +-- +-- This is modelled after Python's os.path library (10.1); see @{04-paths.md|the Guide}. +-- +-- NOTE: the functions assume the paths being dealt with to originate +-- from the OS the application is running on. Windows drive letters are not +-- to be used when running on a Unix system for example. The one exception +-- is Windows paths to allow both forward and backward slashes (since Lua +-- also accepts those) +-- +-- Dependencies: `pl.utils`, `lfs` +-- @module pl.path + +-- imports and locals +local _G = _G +local sub = string.sub +local getenv = os.getenv +local tmpnam = os.tmpname +local package = package +local append, concat, remove = table.insert, table.concat, table.remove +local utils = require 'pl.utils' +local assert_string,raise = utils.assert_string,utils.raise + +local res,lfs = _G.pcall(_G.require,'lfs') +if not res then + error("pl.path requires LuaFileSystem") +end + +local attrib = lfs.attributes +local currentdir = lfs.currentdir +local link_attrib = lfs.symlinkattributes + +local path = {} + +local function err_func(name, param, err, code) + if code == nil then + return ("%s failed for '%s': %s"):format(tostring(name), tostring(param), tostring(err)) + end + return ("%s failed for '%s': %s (code %s)"):format(tostring(name), tostring(param), tostring(err), tostring(code)) +end + +--- Lua iterator over the entries of a given directory. +-- Implicit link to [`luafilesystem.dir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- @function dir +path.dir = lfs.dir + +--- Creates a directory. +-- Implicit link to [`luafilesystem.mkdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- @function mkdir +path.mkdir = function(d) + local ok, err, code = lfs.mkdir(d) + if not ok then + return ok, err_func("mkdir", d, err, code), code + end + return ok, err, code +end + +--- Removes a directory. +-- Implicit link to [`luafilesystem.rmdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- @function rmdir +path.rmdir = function(d) + local ok, err, code = lfs.rmdir(d) + if not ok then + return ok, err_func("rmdir", d, err, code), code + end + return ok, err, code +end + +--- Gets attributes. +-- Implicit link to [`luafilesystem.attributes`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- @function attrib +path.attrib = function(d, r) + local ok, err, code = attrib(d, r) + if not ok then + return ok, err_func("attrib", d, err, code), code + end + return ok, err, code +end + +--- Get the working directory. +-- Implicit link to [`luafilesystem.currentdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- @function currentdir +path.currentdir = function(d) + local ok, err, code = currentdir(d) + if not ok then + return ok, err_func("currentdir", d, err, code), code + end + return ok, err, code +end + +--- Gets symlink attributes. +-- Implicit link to [`luafilesystem.symlinkattributes`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- @function link_attrib +path.link_attrib = function(d, r) + local ok, err, code = link_attrib(d, r) + if not ok then + return ok, err_func("link_attrib", d, err, code), code + end + return ok, err, code +end + +--- Changes the working directory. +-- On Windows, if a drive is specified, it also changes the current drive. If +-- only specifying the drive, it will only switch drive, but not modify the path. +-- Implicit link to [`luafilesystem.chdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- @function chdir +path.chdir = function(d) + local ok, err, code = lfs.chdir(d) + if not ok then + return ok, err_func("chdir", d, err, code), code + end + return ok, err, code +end + +--- is this a directory? +-- @string P A file path +function path.isdir(P) + assert_string(1,P) + if P:match("\\$") then + P = P:sub(1,-2) + end + return attrib(P,'mode') == 'directory' +end + +--- is this a file? +-- @string P A file path +function path.isfile(P) + assert_string(1,P) + return attrib(P,'mode') == 'file' +end + +-- is this a symbolic link? +-- @string P A file path +function path.islink(P) + assert_string(1,P) + if link_attrib then + return link_attrib(P,'mode')=='link' + else + return false + end +end + +--- return size of a file. +-- @string P A file path +function path.getsize(P) + assert_string(1,P) + return attrib(P,'size') +end + +--- does a path exist? +-- @string P A file path +-- @return the file path if it exists (either as file, directory, socket, etc), nil otherwise +function path.exists(P) + assert_string(1,P) + return attrib(P,'mode') ~= nil and P +end + +--- Return the time of last access as the number of seconds since the epoch. +-- @string P A file path +function path.getatime(P) + assert_string(1,P) + return attrib(P,'access') +end + +--- Return the time of last modification as the number of seconds since the epoch. +-- @string P A file path +function path.getmtime(P) + assert_string(1,P) + return attrib(P,'modification') +end + +---Return the system's ctime as the number of seconds since the epoch. +-- @string P A file path +function path.getctime(P) + assert_string(1,P) + return path.attrib(P,'change') +end + + +local function at(s,i) + return sub(s,i,i) +end + +path.is_windows = utils.is_windows + +local sep, other_sep, seps +-- constant sep is the directory separator for this platform. +-- constant dirsep is the separator in the PATH environment variable +if path.is_windows then + path.sep = '\\'; other_sep = '/' + path.dirsep = ';' + seps = { ['/'] = true, ['\\'] = true } +else + path.sep = '/' + path.dirsep = ':' + seps = { ['/'] = true } +end +sep = path.sep + +--- are we running Windows? +-- @class field +-- @name path.is_windows + +--- path separator for this platform. +-- @class field +-- @name path.sep + +--- separator for PATH for this platform +-- @class field +-- @name path.dirsep + +--- given a path, return the directory part and a file part. +-- if there's no directory part, the first value will be empty +-- @string P A file path +-- @return directory part +-- @return file part +-- @usage +-- local dir, file = path.splitpath("some/dir/myfile.txt") +-- assert(dir == "some/dir") +-- assert(file == "myfile.txt") +-- +-- local dir, file = path.splitpath("some/dir/") +-- assert(dir == "some/dir") +-- assert(file == "") +-- +-- local dir, file = path.splitpath("some_dir") +-- assert(dir == "") +-- assert(file == "some_dir") +function path.splitpath(P) + assert_string(1,P) + local i = #P + local ch = at(P,i) + while i > 0 and ch ~= sep and ch ~= other_sep do + i = i - 1 + ch = at(P,i) + end + if i == 0 then + return '',P + else + return sub(P,1,i-1), sub(P,i+1) + end +end + +--- return an absolute path. +-- @string P A file path +-- @string[opt] pwd optional start path to use (default is current dir) +function path.abspath(P,pwd) + assert_string(1,P) + if pwd then assert_string(2,pwd) end + local use_pwd = pwd ~= nil + if not use_pwd and not currentdir() then return P end + P = P:gsub('[\\/]$','') + pwd = pwd or currentdir() + if not path.isabs(P) then + P = path.join(pwd,P) + elseif path.is_windows and not use_pwd and at(P,2) ~= ':' and at(P,2) ~= '\\' then + P = pwd:sub(1,2)..P -- attach current drive to path like '\\fred.txt' + end + return path.normpath(P) +end + +--- given a path, return the root part and the extension part. +-- if there's no extension part, the second value will be empty +-- @string P A file path +-- @treturn string root part (everything upto the "."", maybe empty) +-- @treturn string extension part (including the ".", maybe empty) +-- @usage +-- local file_path, ext = path.splitext("/bonzo/dog_stuff/cat.txt") +-- assert(file_path == "/bonzo/dog_stuff/cat") +-- assert(ext == ".txt") +-- +-- local file_path, ext = path.splitext("") +-- assert(file_path == "") +-- assert(ext == "") +function path.splitext(P) + assert_string(1,P) + local i = #P + local ch = at(P,i) + while i > 0 and ch ~= '.' do + if seps[ch] then + return P,'' + end + i = i - 1 + ch = at(P,i) + end + if i == 0 then + return P,'' + else + return sub(P,1,i-1),sub(P,i) + end +end + +--- return the directory part of a path +-- @string P A file path +-- @treturn string everything before the last dir-separator +-- @see splitpath +-- @usage +-- path.dirname("/some/path/file.txt") -- "/some/path" +-- path.dirname("file.txt") -- "" (empty string) +function path.dirname(P) + assert_string(1,P) + local p1 = path.splitpath(P) + return p1 +end + +--- return the file part of a path +-- @string P A file path +-- @treturn string +-- @see splitpath +-- @usage +-- path.basename("/some/path/file.txt") -- "file.txt" +-- path.basename("/some/path/file/") -- "" (empty string) +function path.basename(P) + assert_string(1,P) + local _,p2 = path.splitpath(P) + return p2 +end + +--- get the extension part of a path. +-- @string P A file path +-- @treturn string +-- @see splitext +-- @usage +-- path.extension("/some/path/file.txt") -- ".txt" +-- path.extension("/some/path/file_txt") -- "" (empty string) +function path.extension(P) + assert_string(1,P) + local _,p2 = path.splitext(P) + return p2 +end + +--- is this an absolute path? +-- @string P A file path +-- @usage +-- path.isabs("hello/path") -- false +-- path.isabs("/hello/path") -- true +-- -- Windows; +-- path.isabs("hello\path") -- false +-- path.isabs("\hello\path") -- true +-- path.isabs("C:\hello\path") -- true +-- path.isabs("C:hello\path") -- false +function path.isabs(P) + assert_string(1,P) + if path.is_windows and at(P,2) == ":" then + return seps[at(P,3)] ~= nil + end + return seps[at(P,1)] ~= nil +end + +--- return the path resulting from combining the individual paths. +-- if the second (or later) path is absolute, we return the last absolute path (joined with any non-absolute paths following). +-- empty elements (except the last) will be ignored. +-- @string p1 A file path +-- @string p2 A file path +-- @string ... more file paths +-- @treturn string the combined path +-- @usage +-- path.join("/first","second","third") -- "/first/second/third" +-- path.join("first","second/third") -- "first/second/third" +-- path.join("/first","/second","third") -- "/second/third" +function path.join(p1,p2,...) + assert_string(1,p1) + assert_string(2,p2) + if select('#',...) > 0 then + local p = path.join(p1,p2) + local args = {...} + for i = 1,#args do + assert_string(i,args[i]) + p = path.join(p,args[i]) + end + return p + end + if path.isabs(p2) then return p2 end + local endc = at(p1,#p1) + if endc ~= path.sep and endc ~= other_sep and endc ~= "" then + p1 = p1..path.sep + end + return p1..p2 +end + +--- normalize the case of a pathname. On Unix, this returns the path unchanged, +-- for Windows it converts; +-- +-- * the path to lowercase +-- * forward slashes to backward slashes +-- @string P A file path +-- @usage path.normcase("/Some/Path/File.txt") +-- -- Windows: "\some\path\file.txt" +-- -- Others : "/Some/Path/File.txt" +function path.normcase(P) + assert_string(1,P) + if path.is_windows then + return P:gsub('/','\\'):lower() + else + return P + end +end + +--- normalize a path name. +-- `A//B`, `A/./B`, and `A/foo/../B` all become `A/B`. +-- +-- An empty path results in '.'. +-- @string P a file path +function path.normpath(P) + assert_string(1,P) + -- Split path into anchor and relative path. + local anchor = '' + if path.is_windows then + if P:match '^\\\\' then -- UNC + anchor = '\\\\' + P = P:sub(3) + elseif seps[at(P, 1)] then + anchor = '\\' + P = P:sub(2) + elseif at(P, 2) == ':' then + anchor = P:sub(1, 2) + P = P:sub(3) + if seps[at(P, 1)] then + anchor = anchor..'\\' + P = P:sub(2) + end + end + P = P:gsub('/','\\') + else + -- According to POSIX, in path start '//' and '/' are distinct, + -- but '///+' is equivalent to '/'. + if P:match '^//' and at(P, 3) ~= '/' then + anchor = '//' + P = P:sub(3) + elseif at(P, 1) == '/' then + anchor = '/' + P = P:match '^/*(.*)$' + end + end + local parts = {} + for part in P:gmatch('[^'..sep..']+') do + if part == '..' then + if #parts ~= 0 and parts[#parts] ~= '..' then + remove(parts) + else + append(parts, part) + end + elseif part ~= '.' then + append(parts, part) + end + end + P = anchor..concat(parts, sep) + if P == '' then P = '.' end + return P +end + +--- relative path from current directory or optional start point +-- @string P a path +-- @string[opt] start optional start point (default current directory) +function path.relpath (P,start) + assert_string(1,P) + if start then assert_string(2,start) end + local split,min,append = utils.split, math.min, table.insert + P = path.abspath(P,start) + start = start or currentdir() + local compare + if path.is_windows then + P = P:gsub("/","\\") + start = start:gsub("/","\\") + compare = function(v) return v:lower() end + else + compare = function(v) return v end + end + local startl, Pl = split(start,sep), split(P,sep) + local n = min(#startl,#Pl) + if path.is_windows and n > 0 and at(Pl[1],2) == ':' and Pl[1] ~= startl[1] then + return P + end + local k = n+1 -- default value if this loop doesn't bail out! + for i = 1,n do + if compare(startl[i]) ~= compare(Pl[i]) then + k = i + break + end + end + local rell = {} + for i = 1, #startl-k+1 do rell[i] = '..' end + if k <= #Pl then + for i = k,#Pl do append(rell,Pl[i]) end + end + return table.concat(rell,sep) +end + + +--- Replace a starting '~' with the user's home directory. +-- In windows, if HOME isn't set, then USERPROFILE is used in preference to +-- HOMEDRIVE HOMEPATH. This is guaranteed to be writeable on all versions of Windows. +-- @string P A file path +function path.expanduser(P) + assert_string(1,P) + if at(P,1) == '~' then + local home = getenv('HOME') + if not home then -- has to be Windows + home = getenv 'USERPROFILE' or (getenv 'HOMEDRIVE' .. getenv 'HOMEPATH') + end + return home..sub(P,2) + else + return P + end +end + + +---Return a suitable full path to a new temporary file name. +-- unlike os.tmpname(), it always gives you a writeable path (uses TEMP environment variable on Windows) +function path.tmpname () + local res = tmpnam() + -- On Windows if Lua is compiled using MSVC14 os.tmpname + -- already returns an absolute path within TEMP env variable directory, + -- no need to prepend it. + if path.is_windows and not res:find(':') then + res = getenv('TEMP')..res + end + return res +end + +--- return the largest common prefix path of two paths. +-- @string path1 a file path +-- @string path2 a file path +-- @return the common prefix (Windows: separators will be normalized, casing will be original) +function path.common_prefix (path1,path2) + assert_string(1,path1) + assert_string(2,path2) + -- get them in order! + if #path1 > #path2 then path2,path1 = path1,path2 end + local compare + if path.is_windows then + path1 = path1:gsub("/", "\\") + path2 = path2:gsub("/", "\\") + compare = function(v) return v:lower() end + else + compare = function(v) return v end + end + for i = 1,#path1 do + if compare(at(path1,i)) ~= compare(at(path2,i)) then + local cp = path1:sub(1,i-1) + if at(path1,i-1) ~= sep then + cp = path.dirname(cp) + end + return cp + end + end + if at(path2,#path1+1) ~= sep then path1 = path.dirname(path1) end + return path1 + --return '' +end + +--- return the full path where a particular Lua module would be found. +-- Both package.path and package.cpath is searched, so the result may +-- either be a Lua file or a shared library. +-- @string mod name of the module +-- @return on success: path of module, lua or binary +-- @return on error: nil, error string listing paths tried +function path.package_path(mod) + assert_string(1,mod) + local res, err1, err2 + res, err1 = package.searchpath(mod,package.path) + if res then return res,true end + res, err2 = package.searchpath(mod,package.cpath) + if res then return res,false end + return raise ('cannot find module on path\n' .. err1 .. "\n" .. err2) +end + + +---- finis ----- +return path diff --git a/framework/lualib/thirdparty/pl/permute.lua b/framework/lualib/thirdparty/pl/permute.lua new file mode 100755 index 0000000..ce646f4 --- /dev/null +++ b/framework/lualib/thirdparty/pl/permute.lua @@ -0,0 +1,196 @@ +--- Permutation operations. +-- +-- Dependencies: `pl.utils`, `pl.tablex` +-- @module pl.permute +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local copy = tablex.deepcopy +local append = table.insert +local assert_arg = utils.assert_arg + + +local permute = {} + + +--- an iterator over all order-permutations of the elements of a list. +-- Please note that the same list is returned each time, so do not keep references! +-- @param a list-like table +-- @return an iterator which provides the next permutation as a list +function permute.order_iter(a) + assert_arg(1,a,'table') + + local t = #a + local stack = { 1 } + local function iter() + local h = #stack + local n = t - h + 1 + + local i = stack[h] + if i > t then + return + end + + if n == 0 then + table.remove(stack) + h = h - 1 + + stack[h] = stack[h] + 1 + return a + + elseif i <= n then + + -- put i-th element as the last one + a[n], a[i] = a[i], a[n] + + -- generate all permutations of the other elements + table.insert(stack, 1) + + else + + table.remove(stack) + h = h - 1 + + n = n + 1 + i = stack[h] + + -- restore i-th element + a[n], a[i] = a[i], a[n] + + stack[h] = stack[h] + 1 + end + return iter() -- tail-call + end + + return iter +end + + +--- construct a table containing all the order-permutations of a list. +-- @param a list-like table +-- @return a table of tables +-- @usage permute.order_table {1,2,3} --> {{2,3,1},{3,2,1},{3,1,2},{1,3,2},{2,1,3},{1,2,3}} +function permute.order_table (a) + assert_arg(1,a,'table') + local res = {} + for t in permute.iter(a) do + append(res,copy(t)) + end + return res +end + + + +--- an iterator over all permutations of the elements of the given lists. +-- @param ... list-like tables, they are nil-safe if a length-field `n` is provided (see `utils.pack`) +-- @return an iterator which provides the next permutation as return values in the same order as the provided lists, preceeded by an index +-- @usage +-- local strs = utils.pack("one", nil, "three") -- adds an 'n' field for nil-safety +-- local bools = utils.pack(true, false) +-- local iter = permute.list_iter(strs, bools) +-- +-- print(iter()) --> 1, one, true +-- print(iter()) --> 2, nil, true +-- print(iter()) --> 3, three, true +-- print(iter()) --> 4, one, false +-- print(iter()) --> 5, nil, false +-- print(iter()) --> 6, three, false +function permute.list_iter(...) + local elements = {...} + local pointers = {} + local sizes = {} + local size = #elements + for i, list in ipairs(elements) do + assert_arg(i,list,'table') + pointers[i] = 1 + sizes[i] = list.n or #list + end + local count = 0 + + return function() + if pointers[size] > sizes[size] then return end -- we're done + count = count + 1 + local r = { n = #elements } + local cascade_up = true + for i = 1, size do + r[i] = elements[i][pointers[i]] + if cascade_up then + pointers[i] = pointers[i] + 1 + if pointers[i] <= sizes[i] then + -- this list is not done yet, stop cascade + cascade_up = false + else + -- this list is done + if i ~= size then + -- reset pointer + pointers[i] = 1 + end + end + end + end + return count, utils.unpack(r) + end +end + + + +--- construct a table containing all the permutations of a set of lists. +-- @param ... list-like tables, they are nil-safe if a length-field `n` is provided +-- @return a list of lists, the sub-lists have an 'n' field for nil-safety +-- @usage +-- local strs = utils.pack("one", nil, "three") -- adds an 'n' field for nil-safety +-- local bools = utils.pack(true, false) +-- local results = permute.list_table(strs, bools) +-- -- results = { +-- -- { "one, true, n = 2 } +-- -- { nil, true, n = 2 }, +-- -- { "three, true, n = 2 }, +-- -- { "one, false, n = 2 }, +-- -- { nil, false, n = 2 }, +-- -- { "three", false, n = 2 }, +-- -- } +function permute.list_table(...) + local iter = permute.list_iter(...) + local results = {} + local i = 1 + while true do + local values = utils.pack(iter()) + if values[1] == nil then return results end + for i = 1, values.n do values[i] = values[i+1] end + values.n = values.n - 1 + results[i] = values + i = i + 1 + end +end + + +-- backward compat, to be deprecated + +--- deprecated. +-- @param ... +-- @see permute.order_iter +function permute.iter(...) + utils.raise_deprecation { + source = "Penlight " .. utils._VERSION, + message = "function 'iter' was renamed to 'order_iter'", + version_removed = "2.0.0", + deprecated_after = "1.9.2", + } + + return permute.order_iter(...) +end + +--- deprecated. +-- @param ... +-- @see permute.order_iter +function permute.table(...) + utils.raise_deprecation { + source = "Penlight " .. utils._VERSION, + message = "function 'table' was renamed to 'order_table'", + version_removed = "2.0.0", + deprecated_after = "1.9.2", + } + + return permute.order_table(...) +end + +return permute diff --git a/framework/lualib/thirdparty/pl/pretty.lua b/framework/lualib/thirdparty/pl/pretty.lua new file mode 100755 index 0000000..09add30 --- /dev/null +++ b/framework/lualib/thirdparty/pl/pretty.lua @@ -0,0 +1,437 @@ +--- Pretty-printing Lua tables. +-- Also provides a sandboxed Lua table reader and +-- a function to present large numbers in human-friendly format. +-- +-- Dependencies: `pl.utils`, `pl.lexer`, `pl.stringx`, `debug` +-- @module pl.pretty + +local append = table.insert +local concat = table.concat +local mfloor, mhuge = math.floor, math.huge +local mtype = math.type +local utils = require 'pl.utils' +local lexer = require 'pl.lexer' +local debug = require 'debug' +local quote_string = require'pl.stringx'.quote_string +local assert_arg = utils.assert_arg + +local original_tostring = tostring + +-- Patch tostring to format numbers with better precision +-- and to produce cross-platform results for +-- infinite values and NaN. +local function tostring(value) + if type(value) ~= "number" then + return original_tostring(value) + elseif value ~= value then + return "NaN" + elseif value == mhuge then + return "Inf" + elseif value == -mhuge then + return "-Inf" + elseif (_VERSION ~= "Lua 5.3" or mtype(value) == "integer") and mfloor(value) == value then + return ("%d"):format(value) + else + local res = ("%.14g"):format(value) + if _VERSION == "Lua 5.3" and mtype(value) == "float" and not res:find("%.") then + -- Number is internally a float but looks like an integer. + -- Insert ".0" after first run of digits. + res = res:gsub("%d+", "%0.0", 1) + end + return res + end +end + +local pretty = {} + +local function save_global_env() + local env = {} + env.hook, env.mask, env.count = debug.gethook() + + -- env.hook is "external hook" if is a C hook function + if env.hook~="external hook" then + debug.sethook() + end + + env.string_mt = getmetatable("") + debug.setmetatable("", nil) + return env +end + +local function restore_global_env(env) + if env then + debug.setmetatable("", env.string_mt) + if env.hook~="external hook" then + debug.sethook(env.hook, env.mask, env.count) + end + end +end + +--- Read a string representation of a Lua table. +-- This function loads and runs the string as Lua code, but bails out +-- if it contains a function definition. +-- Loaded string is executed in an empty environment. +-- @string s string to read in `{...}` format, possibly with some whitespace +-- before or after the curly braces. A single line comment may be present +-- at the beginning. +-- @return a table in case of success. +-- If loading the string failed, return `nil` and error message. +-- If executing loaded string failed, return `nil` and the error it raised. +function pretty.read(s) + assert_arg(1,s,'string') + if s:find '^%s*%-%-' then -- may start with a comment.. + s = s:gsub('%-%-.-\n','') + end + if not s:find '^%s*{' then return nil,"not a Lua table" end + if s:find '[^\'"%w_]function[^\'"%w_]' then + local tok = lexer.lua(s) + for t,v in tok do + if t == 'keyword' and v == 'function' then + return nil,"cannot have functions in table definition" + end + end + end + s = 'return '..s + local chunk,err = utils.load(s,'tbl','t',{}) + if not chunk then return nil,err end + local global_env = save_global_env() + local ok,ret = pcall(chunk) + restore_global_env(global_env) + if ok then return ret + else + return nil,ret + end +end + +--- Read a Lua chunk. +-- @string s Lua code. +-- @tab[opt] env environment used to run the code, empty by default. +-- @bool[opt] paranoid abort loading if any looping constructs a found in the code +-- and disable string methods. +-- @return the environment in case of success or `nil` and syntax or runtime error +-- if something went wrong. +function pretty.load (s, env, paranoid) + env = env or {} + if paranoid then + local tok = lexer.lua(s) + for t,v in tok do + if t == 'keyword' + and (v == 'for' or v == 'repeat' or v == 'function' or v == 'goto') + then + return nil,"looping not allowed" + end + end + end + local chunk,err = utils.load(s,'tbl','t',env) + if not chunk then return nil,err end + local global_env = paranoid and save_global_env() + local ok,err = pcall(chunk) + restore_global_env(global_env) + if not ok then return nil,err end + return env +end + +local function quote_if_necessary (v) + if not v then return '' + else + --AAS + if v:find ' ' then v = quote_string(v) end + end + return v +end + +local keywords + +local function is_identifier (s) + return type(s) == 'string' and s:find('^[%a_][%w_]*$') and not keywords[s] +end + +local function quote (s) + if type(s) == 'table' then + return pretty.write(s,'') + else + --AAS + return quote_string(s)-- ('%q'):format(tostring(s)) + end +end + +local function index (numkey,key) + --AAS + if not numkey then + key = quote(key) + key = key:find("^%[") and (" " .. key .. " ") or key + end + return '['..key..']' +end + + +--- Create a string representation of a Lua table. +-- This function never fails, but may complain by returning an +-- extra value. Normally puts out one item per line, using +-- the provided indent; set the second parameter to an empty string +-- if you want output on one line. +-- +-- *NOTE:* this is NOT a serialization function, not a full blown +-- debug function. Checkout out respectively the +-- [serpent](https://github.com/pkulchenko/serpent) +-- or [inspect](https://github.com/kikito/inspect.lua) +-- Lua modules for that if you need them. +-- @tab tbl Table to serialize to a string. +-- @string[opt] space The indent to use. +-- Defaults to two spaces; pass an empty string for no indentation. +-- @bool[opt] not_clever Pass `true` for plain output, e.g `{['key']=1}`. +-- Defaults to `false`. +-- @return a string +-- @return an optional error message +function pretty.write (tbl,space,not_clever) + if type(tbl) ~= 'table' then + local res = tostring(tbl) + if type(tbl) == 'string' then return quote(tbl) end + return res, 'not a table' + end + if not keywords then + keywords = lexer.get_keywords() + end + local set = ' = ' + if space == '' then set = '=' end + space = space or ' ' + local lines = {} + local line = '' + local tables = {} + + + local function put(s) + if #s > 0 then + line = line..s + end + end + + local function putln (s) + if #line > 0 then + line = line..s + append(lines,line) + line = '' + else + append(lines,s) + end + end + + local function eat_last_comma () + local n = #lines + local lastch = lines[n]:sub(-1,-1) + if lastch == ',' then + lines[n] = lines[n]:sub(1,-2) + end + end + + + -- safe versions for iterators since 5.3+ honors metamethods that can throw + -- errors + local ipairs = function(t) + local i = 0 + local ok, v + local getter = function() return t[i] end + return function() + i = i + 1 + ok, v = pcall(getter) + if v == nil or not ok then return end + return i, t[i] + end + end + local pairs = function(t) + local k, v, ok + local getter = function() return next(t, k) end + return function() + ok, k, v = pcall(getter) + if not ok then return end + return k, v + end + end + + local writeit + writeit = function (t,oldindent,indent) + local tp = type(t) + if tp ~= 'string' and tp ~= 'table' then + putln(quote_if_necessary(tostring(t))..',') + elseif tp == 'string' then + -- if t:find('\n') then + -- putln('[[\n'..t..']],') + -- else + -- putln(quote(t)..',') + -- end + --AAS + putln(quote_string(t) ..",") + elseif tp == 'table' then + if tables[t] then + putln(',') + return + end + tables[t] = true + local newindent = indent..space + putln('{') + local used = {} + if not not_clever then + for i,val in ipairs(t) do + put(indent) + writeit(val,indent,newindent) + used[i] = true + end + end + local ordered_keys = {} + for k,v in pairs(t) do + if type(k) ~= 'number' then + ordered_keys[#ordered_keys + 1] = k + end + end + table.sort(ordered_keys, function (a, b) + if type(a) == type(b) and type(a) == 'string' then + return a < b + end + return type(a) == 'boolean' or (type(b) ~= 'boolean' and type(a) == 'table') + end) + local function write_entry (key, val) + local tkey = type(key) + local numkey = tkey == 'number' + if not_clever then + key = tostring(key) + put(indent..index(numkey,key)..set) + writeit(val,indent,newindent) + else + if not numkey or not used[key] then -- non-array indices + if tkey ~= 'string' then + key = tostring(key) + end + if numkey or not is_identifier(key) then + key = index(numkey,key) + end + put(indent..key..set) + writeit(val,indent,newindent) + end + end + end + for i = 1, #ordered_keys do + local key = ordered_keys[i] + local val = t[key] + write_entry(key, val) + end + for key,val in pairs(t) do + if type(key) == 'number' then + write_entry(key, val) + end + end + tables[t] = nil + eat_last_comma() + putln(oldindent..'},') + else + putln(tostring(t)..',') + end + end + writeit(tbl,'',space) + eat_last_comma() + return concat(lines,#space > 0 and '\n' or '') +end + +--- Dump a Lua table out to a file or stdout. +-- @tab t The table to write to a file or stdout. +-- @string[opt] filename File name to write too. Defaults to writing +-- to stdout. +function pretty.dump (t, filename) + if not filename then + print(pretty.write(t)) + return true + else + return utils.writefile(filename, pretty.write(t)) + end +end + +--- Dump a series of arguments to stdout for debug purposes. +-- This function is attached to the module table `__call` method, to make it +-- extra easy to access. So the full: +-- +-- print(require("pl.pretty").write({...})) +-- +-- Can be shortened to: +-- +-- require"pl.pretty" (...) +-- +-- Any `nil` entries will be printed as `""` to make them explicit. +-- @param ... the parameters to dump to stdout. +-- @usage +-- -- example debug output +-- require"pl.pretty" ("hello", nil, "world", { bye = "world", true} ) +-- +-- -- output: +-- { +-- ["arg 1"] = "hello", +-- ["arg 2"] = "", +-- ["arg 3"] = "world", +-- ["arg 4"] = { +-- true, +-- bye = "world" +-- } +-- } +function pretty.debug(...) + local n = select("#", ...) + local t = { ... } + for i = 1, n do + local value = t[i] + if value == nil then + value = "" + end + t[i] = nil + t["arg " .. i] = value + end + + print(pretty.write(t)) + return true +end + + +local memp,nump = {'B','KiB','MiB','GiB'},{'','K','M','B'} + +local function comma (val) + local thou = math.floor(val/1000) + if thou > 0 then return comma(thou)..','.. tostring(val % 1000) + else return tostring(val) end +end + +--- Format large numbers nicely for human consumption. +-- @number num a number. +-- @string[opt] kind one of `'M'` (memory in `KiB`, `MiB`, etc.), +-- `'N'` (postfixes are `'K'`, `'M'` and `'B'`), +-- or `'T'` (use commas as thousands separator), `'N'` by default. +-- @int[opt] prec number of digits to use for `'M'` and `'N'`, `1` by default. +function pretty.number (num,kind,prec) + local fmt = '%.'..(prec or 1)..'f%s' + if kind == 'T' then + return comma(num) + else + local postfixes, fact + if kind == 'M' then + fact = 1024 + postfixes = memp + else + fact = 1000 + postfixes = nump + end + local div = fact + local k = 1 + while num >= div and k <= #postfixes do + div = div * fact + k = k + 1 + end + div = div / fact + if k > #postfixes then k = k - 1; div = div/fact end + if k > 1 then + return fmt:format(num/div,postfixes[k] or 'duh') + else + return num..postfixes[1] + end + end +end + +return setmetatable(pretty, { + __call = function(self, ...) + return self.debug(...) + end +}) diff --git a/framework/lualib/thirdparty/pl/seq.lua b/framework/lualib/thirdparty/pl/seq.lua new file mode 100755 index 0000000..1c08d20 --- /dev/null +++ b/framework/lualib/thirdparty/pl/seq.lua @@ -0,0 +1,544 @@ +--- Manipulating iterators as sequences. +-- See @{07-functional.md.Sequences|The Guide} +-- +-- Dependencies: `pl.utils`, `pl.types`, `debug` +-- @module pl.seq + +local next,assert,pairs,tonumber,type,setmetatable = next,assert,pairs,tonumber,type,setmetatable +local strfind,format = string.find,string.format +local mrandom = math.random +local tsort,tappend = table.sort,table.insert +local io = io +local utils = require 'pl.utils' +local callable = require 'pl.types'.is_callable +local function_arg = utils.function_arg +local assert_arg = utils.assert_arg +local debug = require 'debug' + +local seq = {} + +-- given a number, return a function(y) which returns true if y > x +-- @param x a number +function seq.greater_than(x) + return function(v) + return tonumber(v) > x + end +end + +-- given a number, returns a function(y) which returns true if y < x +-- @param x a number +function seq.less_than(x) + return function(v) + return tonumber(v) < x + end +end + +-- given any value, return a function(y) which returns true if y == x +-- @param x a value +function seq.equal_to(x) + if type(x) == "number" then + return function(v) + return tonumber(v) == x + end + else + return function(v) + return v == x + end + end +end + +--- given a string, return a function(y) which matches y against the string. +-- @param s a string +function seq.matching(s) + return function(v) + return strfind(v,s) + end +end + +local nexti + +--- sequence adaptor for a table. Note that if any generic function is +-- passed a table, it will automatically use seq.list() +-- @param t a list-like table +-- @usage sum(list(t)) is the sum of all elements of t +-- @usage for x in list(t) do...end +function seq.list(t) + assert_arg(1,t,'table') + if not nexti then + nexti = ipairs{} + end + local key,value = 0 + return function() + key,value = nexti(t,key) + return value + end +end + +--- return the keys of the table. +-- @param t an arbitrary table +-- @return iterator over keys +function seq.keys(t) + assert_arg(1,t,'table') + local key + return function() + key = next(t,key) + return key + end +end + +local list = seq.list +local function default_iter(iter) + if type(iter) == 'table' then return list(iter) + else return iter end +end + +seq.iter = default_iter + +--- create an iterator over a numerical range. Like the standard Python function xrange. +-- @param start a number +-- @param finish a number greater than start +function seq.range(start,finish) + local i = start - 1 + return function() + i = i + 1 + if i > finish then return nil + else return i end + end +end + +-- count the number of elements in the sequence which satisfy the predicate +-- @param iter a sequence +-- @param condn a predicate function (must return either true or false) +-- @param optional argument to be passed to predicate as second argument. +-- @return count +function seq.count(iter,condn,arg) + local i = 0 + seq.foreach(iter,function(val) + if condn(val,arg) then i = i + 1 end + end) + return i +end + +--- return the minimum and the maximum value of the sequence. +-- @param iter a sequence +-- @return minimum value +-- @return maximum value +function seq.minmax(iter) + local vmin,vmax = 1e70,-1e70 + for v in default_iter(iter) do + v = tonumber(v) + if v < vmin then vmin = v end + if v > vmax then vmax = v end + end + return vmin,vmax +end + +--- return the sum and element count of the sequence. +-- @param iter a sequence +-- @param fn an optional function to apply to the values +function seq.sum(iter,fn) + local s = 0 + local i = 0 + for v in default_iter(iter) do + if fn then v = fn(v) end + s = s + v + i = i + 1 + end + return s,i +end + +--- create a table from the sequence. (This will make the result a List.) +-- @param iter a sequence +-- @return a List +-- @usage copy(list(ls)) is equal to ls +-- @usage copy(list {1,2,3}) == List{1,2,3} +function seq.copy(iter) + local res,k = {},1 + for v in default_iter(iter) do + res[k] = v + k = k + 1 + end + setmetatable(res, require('pl.List')) + return res +end + +--- create a table of pairs from the double-valued sequence. +-- @param iter a double-valued sequence +-- @param i1 used to capture extra iterator values +-- @param i2 as with pairs & ipairs +-- @usage copy2(ipairs{10,20,30}) == {{1,10},{2,20},{3,30}} +-- @return a list-like table +function seq.copy2 (iter,i1,i2) + local res,k = {},1 + for v1,v2 in iter,i1,i2 do + res[k] = {v1,v2} + k = k + 1 + end + return res +end + +--- create a table of 'tuples' from a multi-valued sequence. +-- A generalization of copy2 above +-- @param iter a multiple-valued sequence +-- @return a list-like table +function seq.copy_tuples (iter) + iter = default_iter(iter) + local res = {} + local row = {iter()} + while #row > 0 do + tappend(res,row) + row = {iter()} + end + return res +end + +--- return an iterator of random numbers. +-- @param n the length of the sequence +-- @param l same as the first optional argument to math.random +-- @param u same as the second optional argument to math.random +-- @return a sequence +function seq.random(n,l,u) + local rand + assert(type(n) == 'number') + if u then + rand = function() return mrandom(l,u) end + elseif l then + rand = function() return mrandom(l) end + else + rand = mrandom + end + + return function() + if n == 0 then return nil + else + n = n - 1 + return rand() + end + end +end + +--- return an iterator to the sorted elements of a sequence. +-- @param iter a sequence +-- @param comp an optional comparison function (comp(x,y) is true if x < y) +function seq.sort(iter,comp) + local t = seq.copy(iter) + tsort(t,comp) + return list(t) +end + +--- return an iterator which returns elements of two sequences. +-- @param iter1 a sequence +-- @param iter2 a sequence +-- @usage for x,y in seq.zip(ls1,ls2) do....end +function seq.zip(iter1,iter2) + iter1 = default_iter(iter1) + iter2 = default_iter(iter2) + return function() + return iter1(),iter2() + end +end + +--- Makes a table where the key/values are the values and value counts of the sequence. +-- This version works with 'hashable' values like strings and numbers. +-- `pl.tablex.count_map` is more general. +-- @param iter a sequence +-- @return a map-like table +-- @return a table +-- @see pl.tablex.count_map +function seq.count_map(iter) + local t = {} + local v + for s in default_iter(iter) do + v = t[s] + if v then t[s] = v + 1 + else t[s] = 1 end + end + return setmetatable(t, require('pl.Map')) +end + +-- given a sequence, return all the unique values in that sequence. +-- @param iter a sequence +-- @param returns_table true if we return a table, not a sequence +-- @return a sequence or a table; defaults to a sequence. +function seq.unique(iter,returns_table) + local t = seq.count_map(iter) + local res,k = {},1 + for key in pairs(t) do res[k] = key; k = k + 1 end + table.sort(res) + if returns_table then + return res + else + return list(res) + end +end + +--- print out a sequence iter with a separator. +-- @param iter a sequence +-- @param sep the separator (default space) +-- @param nfields maximum number of values per line (default 7) +-- @param fmt optional format function for each value +function seq.printall(iter,sep,nfields,fmt) + local write = io.write + if not sep then sep = ' ' end + if not nfields then + if sep == '\n' then nfields = 1e30 + else nfields = 7 end + end + if fmt then + local fstr = fmt + fmt = function(v) return format(fstr,v) end + end + local k = 1 + for v in default_iter(iter) do + if fmt then v = fmt(v) end + if k < nfields then + write(v,sep) + k = k + 1 + else + write(v,'\n') + k = 1 + end + end + write '\n' +end + +-- return an iterator running over every element of two sequences (concatenation). +-- @param iter1 a sequence +-- @param iter2 a sequence +function seq.splice(iter1,iter2) + iter1 = default_iter(iter1) + iter2 = default_iter(iter2) + local iter = iter1 + return function() + local ret = iter() + if ret == nil then + if iter == iter1 then + iter = iter2 + return iter() + else return nil end + else + return ret + end + end +end + +--- return a sequence where every element of a sequence has been transformed +-- by a function. If you don't supply an argument, then the function will +-- receive both values of a double-valued sequence, otherwise behaves rather like +-- tablex.map. +-- @param fn a function to apply to elements; may take two arguments +-- @param iter a sequence of one or two values +-- @param arg optional argument to pass to function. +function seq.map(fn,iter,arg) + fn = function_arg(1,fn) + iter = default_iter(iter) + return function() + local v1,v2 = iter() + if v1 == nil then return nil end + return fn(v1,arg or v2) or false + end +end + +--- filter a sequence using a predicate function. +-- @param iter a sequence of one or two values +-- @param pred a boolean function; may take two arguments +-- @param arg optional argument to pass to function. +function seq.filter (iter,pred,arg) + pred = function_arg(2,pred) + return function () + local v1,v2 + while true do + v1,v2 = iter() + if v1 == nil then return nil end + if pred(v1,arg or v2) then return v1,v2 end + end + end +end + +--- 'reduce' a sequence using a binary function. +-- @func fn a function of two arguments +-- @param iter a sequence +-- @param initval optional initial value +-- @usage seq.reduce(operator.add,seq.list{1,2,3,4}) == 10 +-- @usage seq.reduce('-',{1,2,3,4,5}) == -13 +function seq.reduce (fn,iter,initval) + fn = function_arg(1,fn) + iter = default_iter(iter) + local val = initval or iter() + if val == nil then return nil end + for v in iter do + val = fn(val,v) + end + return val +end + +--- take the first n values from the sequence. +-- @param iter a sequence of one or two values +-- @param n number of items to take +-- @return a sequence of at most n items +function seq.take (iter,n) + iter = default_iter(iter) + return function() + if n < 1 then return end + local val1,val2 = iter() + if not val1 then return end + n = n - 1 + return val1,val2 + end +end + +--- skip the first n values of a sequence +-- @param iter a sequence of one or more values +-- @param n number of items to skip +function seq.skip (iter,n) + n = n or 1 + for i = 1,n do + if iter() == nil then return list{} end + end + return iter +end + +--- a sequence with a sequence count and the original value. +-- enum(copy(ls)) is a roundabout way of saying ipairs(ls). +-- @param iter a single or double valued sequence +-- @return sequence of (i,v), i = 1..n and v is from iter. +function seq.enum (iter) + local i = 0 + iter = default_iter(iter) + return function () + local val1,val2 = iter() + if not val1 then return end + i = i + 1 + return i,val1,val2 + end +end + +--- map using a named method over a sequence. +-- @param iter a sequence +-- @param name the method name +-- @param arg1 optional first extra argument +-- @param arg2 optional second extra argument +function seq.mapmethod (iter,name,arg1,arg2) + iter = default_iter(iter) + return function() + local val = iter() + if not val then return end + local fn = val[name] + if not fn then error(type(val).." does not have method "..name) end + return fn(val,arg1,arg2) + end +end + +--- a sequence of (last,current) values from another sequence. +-- This will return S(i-1),S(i) if given S(i) +-- @param iter a sequence +function seq.last (iter) + iter = default_iter(iter) + local val, l = iter(), nil + if val == nil then return list{} end + return function () + val,l = iter(),val + if val == nil then return nil end + return val,l + end +end + +--- call the function on each element of the sequence. +-- @param iter a sequence with up to 3 values +-- @param fn a function +function seq.foreach(iter,fn) + fn = function_arg(2,fn) + for i1,i2,i3 in default_iter(iter) do fn(i1,i2,i3) end +end + +---------------------- Sequence Adapters --------------------- + +local SMT + +local function SW (iter,...) + if callable(iter) then + return setmetatable({iter=iter},SMT) + else + return iter,... + end +end + + +-- can't directly look these up in seq because of the wrong argument order... +local map,reduce,mapmethod = seq.map, seq.reduce, seq.mapmethod +local overrides = { + map = function(self,fun,arg) + return map(fun,self,arg) + end, + reduce = function(self,fun,initval) + return reduce(fun,self,initval) + end +} + +SMT = { + __index = function (tbl,key) + local fn = overrides[key] or seq[key] + if fn then + return function(sw,...) return SW(fn(sw.iter,...)) end + else + return function(sw,...) return SW(mapmethod(sw.iter,key,...)) end + end + end, + __call = function (sw) + return sw.iter() + end, +} + +setmetatable(seq,{ + __call = function(tbl,iter,extra) + if not callable(iter) then + if type(iter) == 'table' then iter = seq.list(iter) + else return iter + end + end + if extra then + return setmetatable({iter=function() + return iter(extra) + end},SMT) + else + return setmetatable({iter=iter},SMT) + end + end +}) + +--- create a wrapped iterator over all lines in the file. +-- @param f either a filename, file-like object, or 'STDIN' (for standard input) +-- @param ... for Lua 5.2 only, optional format specifiers, as in `io.read`. +-- @return a sequence wrapper +function seq.lines (f,...) + local iter,obj + if f == 'STDIN' then + f = io.stdin + elseif type(f) == 'string' then + iter,obj = io.lines(f,...) + elseif not f.read then + error("Pass either a string or a file-like object",2) + end + if not iter then + iter,obj = f:lines(...) + end + if obj then -- LuaJIT version returns a function operating on a file + local lines,file = iter,obj + iter = function() return lines(file) end + end + return SW(iter) +end + +function seq.import () + debug.setmetatable(function() end,{ + __index = function(tbl,key) + local s = overrides[key] or seq[key] + if s then return s + else + return function(s,...) return seq.mapmethod(s,key,...) end + end + end + }) +end + +return seq diff --git a/framework/lualib/thirdparty/pl/sip.lua b/framework/lualib/thirdparty/pl/sip.lua new file mode 100755 index 0000000..5d1f2aa --- /dev/null +++ b/framework/lualib/thirdparty/pl/sip.lua @@ -0,0 +1,337 @@ +--- Simple Input Patterns (SIP). +-- SIP patterns start with '$', then a +-- one-letter type, and then an optional variable in curly braces. +-- +-- sip.match('$v=$q','name="dolly"',res) +-- ==> res=={'name','dolly'} +-- sip.match('($q{first},$q{second})','("john","smith")',res) +-- ==> res=={second='smith',first='john'} +-- +-- Type names: +-- +-- v identifier +-- i integer +-- f floating-point +-- q quoted string +-- ([{< match up to closing bracket +-- +-- See @{08-additional.md.Simple_Input_Patterns|the Guide} +-- +-- @module pl.sip + +local loadstring = rawget(_G,'loadstring') or load +local unpack = rawget(_G,'unpack') or rawget(table,'unpack') + +local append,concat = table.insert,table.concat +local ipairs,type = ipairs,type +local io,_G = io,_G +local print,rawget = print,rawget + +local patterns = { + FLOAT = '[%+%-%d]%d*%.?%d*[eE]?[%+%-]?%d*', + INTEGER = '[+%-%d]%d*', + IDEN = '[%a_][%w_]*', + OPTION = '[%a_][%w_%-]*', +} + +local function assert_arg(idx,val,tp) + if type(val) ~= tp then + error("argument "..idx.." must be "..tp, 2) + end +end + +local sip = {} + +local brackets = {['<'] = '>', ['('] = ')', ['{'] = '}', ['['] = ']' } +local stdclasses = {a=1,c=0,d=1,l=1,p=0,u=1,w=1,x=1,s=0} + +local function group(s) + return '('..s..')' +end + +-- escape all magic characters except $, which has special meaning +-- Also, un-escape any characters after $, so $( and $[ passes through as is. +local function escape (spec) + return (spec:gsub('[%-%.%+%[%]%(%)%^%%%?%*]','%%%0'):gsub('%$%%(%S)','$%1')) +end + +-- Most spaces within patterns can match zero or more spaces. +-- Spaces between alphanumeric characters or underscores or between +-- patterns that can match these characters, however, must match at least +-- one space. Otherwise '$v $v' would match 'abcd' as {'abc', 'd'}. +-- This function replaces continuous spaces within a pattern with either +-- '%s*' or '%s+' according to this rule. The pattern has already +-- been stripped of pattern names by now. +local function compress_spaces(patt) + return (patt:gsub("()%s+()", function(i1, i2) + local before = patt:sub(i1 - 2, i1 - 1) + if before:match('%$[vifadxlu]') or before:match('^[^%$]?[%w_]$') then + local after = patt:sub(i2, i2 + 1) + if after:match('%$[vifadxlu]') or after:match('^[%w_]') then + return '%s+' + end + end + return '%s*' + end)) +end + +local pattern_map = { + v = group(patterns.IDEN), + i = group(patterns.INTEGER), + f = group(patterns.FLOAT), + o = group(patterns.OPTION), + r = '(%S.*)', + p = '([%a]?[:]?[\\/%.%w_]+)' +} + +function sip.custom_pattern(flag,patt) + pattern_map[flag] = patt +end + +--- convert a SIP pattern into the equivalent Lua string pattern. +-- @param spec a SIP pattern +-- @param options a table; only the at_start field is +-- currently meaningful and ensures that the pattern is anchored +-- at the start of the string. +-- @return a Lua string pattern. +function sip.create_pattern (spec,options) + assert_arg(1,spec,'string') + local fieldnames,fieldtypes = {},{} + + if type(spec) == 'string' then + spec = escape(spec) + else + local res = {} + for i,s in ipairs(spec) do + res[i] = escape(s) + end + spec = concat(res,'.-') + end + + local kount = 1 + + local function addfield (name,type) + name = name or kount + append(fieldnames,name) + fieldtypes[name] = type + kount = kount + 1 + end + + local named_vars = spec:find('{%a+}') + + if options and options.at_start then + spec = '^'..spec + end + if spec:sub(-1,-1) == '$' then + spec = spec:sub(1,-2)..'$r' + if named_vars then spec = spec..'{rest}' end + end + + local names + + if named_vars then + names = {} + spec = spec:gsub('{(%a+)}',function(name) + append(names,name) + return '' + end) + end + spec = compress_spaces(spec) + + local k = 1 + local err + local r = (spec:gsub('%$%S',function(s) + local type,name + type = s:sub(2,2) + if names then name = names[k]; k=k+1 end + -- this kludge is necessary because %q generates two matches, and + -- we want to ignore the first. Not a problem for named captures. + if not names and type == 'q' then + addfield(nil,'Q') + else + addfield(name,type) + end + local res + if pattern_map[type] then + res = pattern_map[type] + elseif type == 'q' then + -- some Lua pattern matching voodoo; we want to match '...' as + -- well as "...", and can use the fact that %n will match a + -- previous capture. Adding the extra field above comes from needing + -- to accommodate the extra spurious match (which is either ' or ") + addfield(name,type) + res = '(["\'])(.-)%'..(kount-2) + else + local endbracket = brackets[type] + if endbracket then + res = '(%b'..type..endbracket..')' + elseif stdclasses[type] or stdclasses[type:lower()] then + res = '(%'..type..'+)' + else + err = "unknown format type or character class" + end + end + return res + end)) + + if err then + return nil,err + else + return r,fieldnames,fieldtypes + end +end + + +local function tnumber (s) + return s == 'd' or s == 'i' or s == 'f' +end + +function sip.create_spec_fun(spec,options) + local fieldtypes,fieldnames + local ls = {} + spec,fieldnames,fieldtypes = sip.create_pattern(spec,options) + if not spec then return spec,fieldnames end + local named_vars = type(fieldnames[1]) == 'string' + for i = 1,#fieldnames do + append(ls,'mm'..i) + end + ls[1] = ls[1] or "mm1" -- behave correctly if there are no patterns + local fun = ('return (function(s,res)\n\tlocal %s = s:match(%q)\n'):format(concat(ls,','),spec) + fun = fun..'\tif not mm1 then return false end\n' + local k=1 + for i,f in ipairs(fieldnames) do + if f ~= '_' then + local var = 'mm'..i + if tnumber(fieldtypes[f]) then + var = 'tonumber('..var..')' + elseif brackets[fieldtypes[f]] then + var = var..':sub(2,-2)' + end + if named_vars then + fun = ('%s\tres.%s = %s\n'):format(fun,f,var) + else + if fieldtypes[f] ~= 'Q' then -- we skip the string-delim capture + fun = ('%s\tres[%d] = %s\n'):format(fun,k,var) + k = k + 1 + end + end + end + end + return fun..'\treturn true\nend)\n', named_vars +end + +--- convert a SIP pattern into a matching function. +-- The returned function takes two arguments, the line and an empty table. +-- If the line matched the pattern, then this function returns true +-- and the table is filled with field-value pairs. +-- @param spec a SIP pattern +-- @param options optional table; {at_start=true} ensures that the pattern +-- is anchored at the start of the string. +-- @return a function if successful, or nil,error +function sip.compile(spec,options) + assert_arg(1,spec,'string') + local fun,names = sip.create_spec_fun(spec,options) + if not fun then return nil,names end + if rawget(_G,'_DEBUG') then print(fun) end + local chunk,err = loadstring(fun,'tmp') + if err then return nil,err end + return chunk(),names +end + +local cache = {} + +--- match a SIP pattern against a string. +-- @param spec a SIP pattern +-- @param line a string +-- @param res a table to receive values +-- @param options (optional) option table +-- @return true or false +function sip.match (spec,line,res,options) + assert_arg(1,spec,'string') + assert_arg(2,line,'string') + assert_arg(3,res,'table') + if not cache[spec] then + cache[spec] = sip.compile(spec,options) + end + return cache[spec](line,res) +end + +--- match a SIP pattern against the start of a string. +-- @param spec a SIP pattern +-- @param line a string +-- @param res a table to receive values +-- @return true or false +function sip.match_at_start (spec,line,res) + return sip.match(spec,line,res,{at_start=true}) +end + +--- given a pattern and a file object, return an iterator over the results +-- @param spec a SIP pattern +-- @param f a file-like object. +function sip.fields (spec,f) + assert_arg(1,spec,'string') + if not f then return nil,"no file object" end + local fun,err = sip.compile(spec) + if not fun then return nil,err end + local res = {} + return function() + while true do + local line = f:read() + if not line then return end + if fun(line,res) then + local values = res + res = {} + return unpack(values) + end + end + end +end + +local read_patterns = {} + +--- register a match which will be used in the read function. +-- @string spec a SIP pattern +-- @func fun a function to be called with the results of the match +-- @see read +function sip.pattern (spec,fun) + assert_arg(1,spec,'string') + local pat,named = sip.compile(spec) + append(read_patterns,{pat=pat,named=named,callback=fun}) +end + +--- enter a loop which applies all registered matches to the input file. +-- @param f a file-like object +-- @array matches optional list of `{spec,fun}` pairs, as for `pattern` above. +function sip.read (f,matches) + local owned,err + if not f then return nil,"no file object" end + if type(f) == 'string' then + f,err = io.open(f) + if not f then return nil,err end + owned = true + end + if matches then + for _,p in ipairs(matches) do + sip.pattern(p[1],p[2]) + end + end + local res = {} + for line in f:lines() do + for _,item in ipairs(read_patterns) do + if item.pat(line,res) then + if item.callback then + if item.named then + item.callback(res) + else + item.callback(unpack(res)) + end + end + res = {} + break + end + end + end + if owned then f:close() end +end + +return sip diff --git a/framework/lualib/thirdparty/pl/strict.lua b/framework/lualib/thirdparty/pl/strict.lua new file mode 100755 index 0000000..67cbf5b --- /dev/null +++ b/framework/lualib/thirdparty/pl/strict.lua @@ -0,0 +1,138 @@ +--- Checks uses of undeclared global variables. +-- All global variables must be 'declared' through a regular assignment +-- (even assigning `nil` will do) in a main chunk before being used +-- anywhere or assigned to inside a function. Existing metatables `__newindex` and `__index` +-- metamethods are respected. +-- +-- You can set any table to have strict behaviour using `strict.module`. Creating a new +-- module with `strict.closed_module` makes the module immune to monkey-patching, if +-- you don't wish to encourage monkey business. +-- +-- If the global `PENLIGHT_NO_GLOBAL_STRICT` is defined, then this module won't make the +-- global environment strict - if you just want to explicitly set table strictness. +-- +-- @module pl.strict + +require 'debug' -- for Lua 5.2 +local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget +local strict = {} + +local function what () + local d = getinfo(3, "S") + return d and d.what or "C" +end + +--- make an existing table strict. +-- @string[opt] name name of table +-- @tab[opt] mod the table to protect - if `nil` then we'll return a new table +-- @tab[opt] predeclared - table of variables that are to be considered predeclared. +-- @return the given table, or a new table +-- @usage +-- local M = { hello = "world" } +-- strict.module ("Awesome_Module", M, { +-- Lua = true, -- defines allowed keys +-- }) +-- +-- assert(M.hello == "world") +-- assert(M.Lua == nil) -- access allowed, but has no value yet +-- M.Lua = "Rocks" +-- assert(M.Lua == "Rocks") +-- M.not_allowed = "bad boy" -- throws an error +function strict.module (name,mod,predeclared) + local mt, old_newindex, old_index, old_index_type, global + if predeclared then + global = predeclared.__global + end + if type(mod) == 'table' then + mt = getmetatable(mod) + if mt and rawget(mt,'__declared') then return end -- already patched... + else + mod = {} + end + if mt == nil then + mt = {} + setmetatable(mod, mt) + else + old_newindex = mt.__newindex + old_index = mt.__index + old_index_type = type(old_index) + end + mt.__declared = predeclared or {} + mt.__newindex = function(t, n, v) + if old_newindex then + old_newindex(t, n, v) + if rawget(t,n)~=nil then return end + end + if not mt.__declared[n] then + if global then + local w = what() + if w ~= "main" and w ~= "C" then + error("assign to undeclared global '"..n.."'", 2) + end + end + mt.__declared[n] = true + end + rawset(t, n, v) + end + mt.__index = function(t,n) + if not mt.__declared[n] and what() ~= "C" then + if old_index then + if old_index_type == "table" then + local fallback = old_index[n] + if fallback ~= nil then + return fallback + end + else + local res = old_index(t, n) + if res ~= nil then + return res + end + end + end + local msg = "variable '"..n.."' is not declared" + if name then + msg = msg .. " in '"..tostring(name).."'" + end + error(msg, 2) + end + return rawget(t, n) + end + return mod +end + +--- make all tables in a table strict. +-- So `strict.make_all_strict(_G)` prevents monkey-patching +-- of any global table +-- @tab T the table containing the tables to protect. Table `T` itself will NOT be protected. +function strict.make_all_strict (T) + for k,v in pairs(T) do + if type(v) == 'table' and v ~= T then + strict.module(k,v) + end + end +end + +--- make a new module table which is closed to further changes. +-- @tab mod module table +-- @string name module name +function strict.closed_module (mod,name) + -- No clue to what this is useful for? see tests + -- Deprecate this and remove??? + local M = {} + mod = mod or {} + local mt = getmetatable(mod) + if not mt then + mt = {} + setmetatable(mod,mt) + end + mt.__newindex = function(t,k,v) + M[k] = v + end + return strict.module(name,M) +end + +if not rawget(_G,'PENLIGHT_NO_GLOBAL_STRICT') then + strict.module(nil,_G,{_PROMPT=true,_PROMPT2=true,__global=true}) +end + +return strict diff --git a/framework/lualib/thirdparty/pl/stringio.lua b/framework/lualib/thirdparty/pl/stringio.lua new file mode 100755 index 0000000..666f415 --- /dev/null +++ b/framework/lualib/thirdparty/pl/stringio.lua @@ -0,0 +1,158 @@ +--- Reading and writing strings using file-like objects.
+-- +-- f = stringio.open(text) +-- l1 = f:read() -- read first line +-- n,m = f:read ('*n','*n') -- read two numbers +-- for line in f:lines() do print(line) end -- iterate over all lines +-- f = stringio.create() +-- f:write('hello') +-- f:write('dolly') +-- assert(f:value(),'hellodolly') +-- +-- See @{03-strings.md.File_style_I_O_on_Strings|the Guide}. +-- @module pl.stringio + +local unpack = rawget(_G,'unpack') or rawget(table,'unpack') +local tonumber = tonumber +local concat,append = table.concat,table.insert + +local stringio = {} + +-- Writer class +local SW = {} +SW.__index = SW + +local function xwrite(self,...) + local args = {...} --arguments may not be nil! + for i = 1, #args do + append(self.tbl,args[i]) + end +end + +function SW:write(arg1,arg2,...) + if arg2 then + xwrite(self,arg1,arg2,...) + else + append(self.tbl,arg1) + end +end + +function SW:writef(fmt,...) + self:write(fmt:format(...)) +end + +function SW:value() + return concat(self.tbl) +end + +function SW:__tostring() + return self:value() +end + +function SW:close() -- for compatibility only +end + +function SW:seek() +end + +-- Reader class +local SR = {} +SR.__index = SR + +function SR:_read(fmt) + local i,str = self.i,self.str + local sz = #str + if i > sz then return nil end + local res + if fmt == '*l' or fmt == '*L' then + local idx = str:find('\n',i) or (sz+1) + res = str:sub(i,fmt == '*l' and idx-1 or idx) + self.i = idx+1 + elseif fmt == '*a' then + res = str:sub(i) + self.i = sz + elseif fmt == '*n' then + local _,i2,idx + _,idx = str:find ('%s*%d+',i) + _,i2 = str:find ('^%.%d+',idx+1) + if i2 then idx = i2 end + _,i2 = str:find ('^[eE][%+%-]*%d+',idx+1) + if i2 then idx = i2 end + local val = str:sub(i,idx) + res = tonumber(val) + self.i = idx+1 + elseif type(fmt) == 'number' then + res = str:sub(i,i+fmt-1) + self.i = i + fmt + else + error("bad read format",2) + end + return res +end + +function SR:read(...) + if select('#',...) == 0 then + return self:_read('*l') + else + local res, fmts = {},{...} + for i = 1, #fmts do + res[i] = self:_read(fmts[i]) + end + return unpack(res) + end +end + +function SR:seek(whence,offset) + local base + whence = whence or 'cur' + offset = offset or 0 + if whence == 'set' then + base = 1 + elseif whence == 'cur' then + base = self.i + elseif whence == 'end' then + base = #self.str + end + self.i = base + offset + return self.i +end + +function SR:lines(...) + local n, args = select('#',...) + if n > 0 then + args = {...} + end + return function() + if n == 0 then + return self:_read '*l' + else + return self:read(unpack(args)) + end + end +end + +function SR:close() -- for compatibility only +end + +--- create a file-like object which can be used to construct a string. +-- The resulting object has an extra `value()` method for +-- retrieving the string value. Implements `file:write`, `file:seek`, `file:lines`, +-- plus an extra `writef` method which works like `utils.printf`. +-- @usage f = create(); f:write('hello, dolly\n'); print(f:value()) +function stringio.create() + return setmetatable({tbl={}},SW) +end + +--- create a file-like object for reading from a given string. +-- Implements `file:read`. +-- @string s The input string. +-- @usage fs = open '20 10'; x,y = f:read ('*n','*n'); assert(x == 20 and y == 10) +function stringio.open(s) + return setmetatable({str=s,i=1},SR) +end + +function stringio.lines(s,...) + return stringio.open(s):lines(...) +end + +return stringio diff --git a/framework/lualib/thirdparty/pl/stringx.lua b/framework/lualib/thirdparty/pl/stringx.lua new file mode 100755 index 0000000..52a134f --- /dev/null +++ b/framework/lualib/thirdparty/pl/stringx.lua @@ -0,0 +1,594 @@ +--- Python-style extended string library. +-- +-- see 3.6.1 of the Python reference. +-- If you want to make these available as string methods, then say +-- `stringx.import()` to bring them into the standard `string` table. +-- +-- See @{03-strings.md|the Guide} +-- +-- Dependencies: `pl.utils` +-- @module pl.stringx +local utils = require 'pl.utils' +local string = string +local find = string.find +local type,setmetatable,ipairs = type,setmetatable,ipairs +local error = error +local gsub = string.gsub +local rep = string.rep +local sub = string.sub +local reverse = string.reverse +local concat = table.concat +local append = table.insert +local escape = utils.escape +local ceil, max = math.ceil, math.max +local assert_arg,usplit = utils.assert_arg,utils.split +local lstrip + +local function assert_string (n,s) + assert_arg(n,s,'string') +end + +local function non_empty(s) + return #s > 0 +end + +local function assert_nonempty_string(n,s) + assert_arg(n,s,'string',non_empty,'must be a non-empty string') +end + +local function makelist(l) + return setmetatable(l, require('pl.List')) +end + +local stringx = {} + +------------------ +-- String Predicates +-- @section predicates + +--- does s only contain alphabetic characters? +-- @string s a string +function stringx.isalpha(s) + assert_string(1,s) + return find(s,'^%a+$') == 1 +end + +--- does s only contain digits? +-- @string s a string +function stringx.isdigit(s) + assert_string(1,s) + return find(s,'^%d+$') == 1 +end + +--- does s only contain alphanumeric characters? +-- @string s a string +function stringx.isalnum(s) + assert_string(1,s) + return find(s,'^%w+$') == 1 +end + +--- does s only contain spaces? +-- @string s a string +function stringx.isspace(s) + assert_string(1,s) + return find(s,'^%s+$') == 1 +end + +--- does s only contain lower case characters? +-- @string s a string +function stringx.islower(s) + assert_string(1,s) + return find(s,'^[%l%s]+$') == 1 +end + +--- does s only contain upper case characters? +-- @string s a string +function stringx.isupper(s) + assert_string(1,s) + return find(s,'^[%u%s]+$') == 1 +end + +local function raw_startswith(s, prefix) + return find(s,prefix,1,true) == 1 +end + +local function raw_endswith(s, suffix) + return #s >= #suffix and find(s, suffix, #s-#suffix+1, true) and true or false +end + +local function test_affixes(s, affixes, fn) + if type(affixes) == 'string' then + return fn(s,affixes) + elseif type(affixes) == 'table' then + for _,affix in ipairs(affixes) do + if fn(s,affix) then return true end + end + return false + else + error(("argument #2 expected a 'string' or a 'table', got a '%s'"):format(type(affixes))) + end +end + +--- does s start with prefix or one of prefixes? +-- @string s a string +-- @param prefix a string or an array of strings +function stringx.startswith(s,prefix) + assert_string(1,s) + return test_affixes(s,prefix,raw_startswith) +end + +--- does s end with suffix or one of suffixes? +-- @string s a string +-- @param suffix a string or an array of strings +function stringx.endswith(s,suffix) + assert_string(1,s) + return test_affixes(s,suffix,raw_endswith) +end + +--- Strings and Lists +-- @section lists + +--- concatenate the strings using this string as a delimiter. +-- Note that the arguments are reversed from `string.concat`. +-- @string s the string +-- @param seq a table of strings or numbers +-- @usage stringx.join(' ', {1,2,3}) == '1 2 3' +function stringx.join(s,seq) + assert_string(1,s) + return concat(seq,s) +end + +--- Split a string into a list of lines. +-- `"\r"`, `"\n"`, and `"\r\n"` are considered line ends. +-- They are not included in the lines unless `keepends` is passed. +-- Terminal line end does not produce an extra line. +-- Splitting an empty string results in an empty list. +-- @string s the string. +-- @bool[opt] keep_ends include line ends. +-- @return List of lines +function stringx.splitlines(s, keep_ends) + assert_string(1, s) + local res = {} + local pos = 1 + while true do + local line_end_pos = find(s, '[\r\n]', pos) + if not line_end_pos then + break + end + + local line_end = sub(s, line_end_pos, line_end_pos) + if line_end == '\r' and sub(s, line_end_pos + 1, line_end_pos + 1) == '\n' then + line_end = '\r\n' + end + + local line = sub(s, pos, line_end_pos - 1) + if keep_ends then + line = line .. line_end + end + append(res, line) + + pos = line_end_pos + #line_end + end + + if pos <= #s then + append(res, sub(s, pos)) + end + return makelist(res) +end + +--- split a string into a list of strings using a delimiter. +-- @function split +-- @string s the string +-- @string[opt] re a delimiter (defaults to whitespace) +-- @int[opt] n maximum number of results +-- @return List +-- @usage #(stringx.split('one two')) == 2 +-- @usage stringx.split('one,two,three', ',') == List{'one','two','three'} +-- @usage stringx.split('one,two,three', ',', 2) == List{'one','two,three'} +function stringx.split(s,re,n) + assert_string(1,s) + local plain = true + if not re then -- default spaces + s = lstrip(s) + plain = false + end + local res = usplit(s,re,plain,n) + if re and re ~= '' and + find(s,re,-#re,true) and + (n or math.huge) > #res then + res[#res+1] = "" + end + return makelist(res) +end + +--- replace all tabs in s with tabsize spaces. If not specified, tabsize defaults to 8. +-- Tab stops will be honored. +-- @string s the string +-- @int tabsize[opt=8] number of spaces to expand each tab +-- @return expanded string +-- @usage stringx.expandtabs('\tone,two,three', 4) == ' one,two,three' +-- @usage stringx.expandtabs(' \tone,two,three', 4) == ' one,two,three' +function stringx.expandtabs(s,tabsize) + assert_string(1,s) + tabsize = tabsize or 8 + return (s:gsub("([^\t\r\n]*)\t", function(before_tab) + return before_tab .. (" "):rep(tabsize - #before_tab % tabsize) + end)) +end + +--- Finding and Replacing +-- @section find + +local function _find_all(s,sub,first,last,allow_overlap) + first = first or 1 + last = last or #s + if sub == '' then return last+1,last-first+1 end + local i1,i2 = find(s,sub,first,true) + local res + local k = 0 + while i1 do + if last and i2 > last then break end + res = i1 + k = k + 1 + if allow_overlap then + i1,i2 = find(s,sub,i1+1,true) + else + i1,i2 = find(s,sub,i2+1,true) + end + end + return res,k +end + +--- find index of first instance of sub in s from the left. +-- @string s the string +-- @string sub substring +-- @int[opt] first first index +-- @int[opt] last last index +-- @return start index, or nil if not found +function stringx.lfind(s,sub,first,last) + assert_string(1,s) + assert_string(2,sub) + local i1, i2 = find(s,sub,first,true) + + if i1 and (not last or i2 <= last) then + return i1 + else + return nil + end +end + +--- find index of first instance of sub in s from the right. +-- @string s the string +-- @string sub substring +-- @int[opt] first first index +-- @int[opt] last last index +-- @return start index, or nil if not found +function stringx.rfind(s,sub,first,last) + assert_string(1,s) + assert_string(2,sub) + return (_find_all(s,sub,first,last,true)) +end + +--- replace up to n instances of old by new in the string s. +-- If n is not present, replace all instances. +-- @string s the string +-- @string old the target substring +-- @string new the substitution +-- @int[opt] n optional maximum number of substitutions +-- @return result string +function stringx.replace(s,old,new,n) + assert_string(1,s) + assert_string(2,old) + assert_string(3,new) + return (gsub(s,escape(old),new:gsub('%%','%%%%'),n)) +end + +--- count all instances of substring in string. +-- @string s the string +-- @string sub substring +-- @bool[opt] allow_overlap allow matches to overlap +-- @usage +-- assert(stringx.count('banana', 'ana') == 1) +-- assert(stringx.count('banana', 'ana', true) == 2) +function stringx.count(s,sub,allow_overlap) + assert_string(1,s) + local _,k = _find_all(s,sub,1,false,allow_overlap) + return k +end + +--- Stripping and Justifying +-- @section strip + +local function _just(s,w,ch,left,right) + local n = #s + if w > n then + if not ch then ch = ' ' end + local f1,f2 + if left and right then + local rn = ceil((w-n)/2) + local ln = w - n - rn + f1 = rep(ch,ln) + f2 = rep(ch,rn) + elseif right then + f1 = rep(ch,w-n) + f2 = '' + else + f2 = rep(ch,w-n) + f1 = '' + end + return f1..s..f2 + else + return s + end +end + +--- left-justify s with width w. +-- @string s the string +-- @int w width of justification +-- @string[opt=' '] ch padding character +-- @usage stringx.ljust('hello', 10, '*') == '*****hello' +function stringx.ljust(s,w,ch) + assert_string(1,s) + assert_arg(2,w,'number') + return _just(s,w,ch,true,false) +end + +--- right-justify s with width w. +-- @string s the string +-- @int w width of justification +-- @string[opt=' '] ch padding character +-- @usage stringx.rjust('hello', 10, '*') == 'hello*****' +function stringx.rjust(s,w,ch) + assert_string(1,s) + assert_arg(2,w,'number') + return _just(s,w,ch,false,true) +end + +--- center-justify s with width w. +-- @string s the string +-- @int w width of justification +-- @string[opt=' '] ch padding character +-- @usage stringx.center('hello', 10, '*') == '**hello***' +function stringx.center(s,w,ch) + assert_string(1,s) + assert_arg(2,w,'number') + return _just(s,w,ch,true,true) +end + +local function _strip(s,left,right,chrs) + if not chrs then + chrs = '%s' + else + chrs = '['..escape(chrs)..']' + end + local f = 1 + local t + if left then + local i1,i2 = find(s,'^'..chrs..'*') + if i2 >= i1 then + f = i2+1 + end + end + if right then + if #s < 200 then + local i1,i2 = find(s,chrs..'*$',f) + if i2 >= i1 then + t = i1-1 + end + else + local rs = reverse(s) + local i1,i2 = find(rs, '^'..chrs..'*') + if i2 >= i1 then + t = -i2 + end + end + end + return sub(s,f,t) +end + +--- trim any whitespace on the left of s. +-- @string s the string +-- @string[opt='%s'] chrs default any whitespace character, +-- but can be a string of characters to be trimmed +function stringx.lstrip(s,chrs) + assert_string(1,s) + return _strip(s,true,false,chrs) +end +lstrip = stringx.lstrip + +--- trim any whitespace on the right of s. +-- @string s the string +-- @string[opt='%s'] chrs default any whitespace character, +-- but can be a string of characters to be trimmed +function stringx.rstrip(s,chrs) + assert_string(1,s) + return _strip(s,false,true,chrs) +end + +--- trim any whitespace on both left and right of s. +-- @string s the string +-- @string[opt='%s'] chrs default any whitespace character, +-- but can be a string of characters to be trimmed +function stringx.strip(s,chrs) + assert_string(1,s) + return _strip(s,true,true,chrs) +end + +--- Partioning Strings +-- @section partioning + +--- split a string using a pattern. Note that at least one value will be returned! +-- @string s the string +-- @string[opt='%s'] re a Lua string pattern (defaults to whitespace) +-- @return the parts of the string +-- @usage a,b = line:splitv('=') +-- @see utils.splitv +function stringx.splitv(s,re) + assert_string(1,s) + return utils.splitv(s,re) +end + +-- The partition functions split a string using a delimiter into three parts: +-- the part before, the delimiter itself, and the part afterwards +local function _partition(p,delim,fn) + local i1,i2 = fn(p,delim) + if not i1 or i1 == -1 then + return p,'','' + else + if not i2 then i2 = i1 end + return sub(p,1,i1-1),sub(p,i1,i2),sub(p,i2+1) + end +end + +--- partition the string using first occurance of a delimiter +-- @string s the string +-- @string ch delimiter +-- @return part before ch +-- @return ch +-- @return part after ch +-- @usage {stringx.partition('a,b,c', ','))} == {'a', ',', 'b,c'} +-- @usage {stringx.partition('abc', 'x'))} == {'abc', '', ''} +function stringx.partition(s,ch) + assert_string(1,s) + assert_nonempty_string(2,ch) + return _partition(s,ch,stringx.lfind) +end + +--- partition the string p using last occurance of a delimiter +-- @string s the string +-- @string ch delimiter +-- @return part before ch +-- @return ch +-- @return part after ch +-- @usage {stringx.rpartition('a,b,c', ','))} == {'a,b', ',', 'c'} +-- @usage {stringx.rpartition('abc', 'x'))} == {'', '', 'abc'} +function stringx.rpartition(s,ch) + assert_string(1,s) + assert_nonempty_string(2,ch) + local a,b,c = _partition(s,ch,stringx.rfind) + if a == s then -- no match found + return c,b,a + end + return a,b,c +end + +--- return the 'character' at the index. +-- @string s the string +-- @int idx an index (can be negative) +-- @return a substring of length 1 if successful, empty string otherwise. +function stringx.at(s,idx) + assert_string(1,s) + assert_arg(2,idx,'number') + return sub(s,idx,idx) +end + +--- Miscelaneous +-- @section misc + +--- return an iterator over all lines in a string +-- @string s the string +-- @return an iterator +-- @usage +-- local line_no = 1 +-- for line in stringx.lines(some_text) do +-- print(line_no, line) +-- line_no = line_no + 1 +-- end +function stringx.lines(s) + assert_string(1,s) + if not s:find '\n$' then s = s..'\n' end + return s:gmatch('([^\n]*)\n') +end + +--- inital word letters uppercase ('title case'). +-- Here 'words' mean chunks of non-space characters. +-- @string s the string +-- @return a string with each word's first letter uppercase +-- @usage stringx.title("hello world") == "Hello World") +function stringx.title(s) + assert_string(1,s) + return (s:gsub('(%S)(%S*)',function(f,r) + return f:upper()..r:lower() + end)) +end + +stringx.capitalize = stringx.title + +local ellipsis = '...' +local n_ellipsis = #ellipsis + +--- Return a shortened version of a string. +-- Fits string within w characters. Removed characters are marked with ellipsis. +-- @string s the string +-- @int w the maxinum size allowed +-- @bool tail true if we want to show the end of the string (head otherwise) +-- @usage ('1234567890'):shorten(8) == '12345...' +-- @usage ('1234567890'):shorten(8, true) == '...67890' +-- @usage ('1234567890'):shorten(20) == '1234567890' +function stringx.shorten(s,w,tail) + assert_string(1,s) + if #s > w then + if w < n_ellipsis then return ellipsis:sub(1,w) end + if tail then + local i = #s - w + 1 + n_ellipsis + return ellipsis .. s:sub(i) + else + return s:sub(1,w-n_ellipsis) .. ellipsis + end + end + return s +end + +--- Utility function that finds any patterns that match a long string's an open or close. +-- Note that having this function use the least number of equal signs that is possible is a harder algorithm to come up with. +-- Right now, it simply returns the greatest number of them found. +-- @param s The string +-- @return 'nil' if not found. If found, the maximum number of equal signs found within all matches. +local function has_lquote(s) + local lstring_pat = '([%[%]])(=*)%1' + local equals, new_equals, _ + local finish = 1 + repeat + _, finish, _, new_equals = s:find(lstring_pat, finish) + if new_equals then + equals = max(equals or 0, #new_equals) + end + until not new_equals + + return equals +end + +--- Quote the given string and preserve any control or escape characters, such that reloading the string in Lua returns the same result. +-- @param s The string to be quoted. +-- @return The quoted string. +function stringx.quote_string(s) + assert_string(1,s) + -- Find out if there are any embedded long-quote sequences that may cause issues. + -- This is important when strings are embedded within strings, like when serializing. + -- Append a closing bracket to catch unfinished long-quote sequences at the end of the string. + local equal_signs = has_lquote(s .. "]") + + -- Note that strings containing "\r" can't be quoted using long brackets + -- as Lua lexer converts all newlines to "\n" within long strings. + if (s:find("\n") or equal_signs) and not s:find("\r") then + -- If there is an embedded sequence that matches a long quote, then + -- find the one with the maximum number of = signs and add one to that number. + equal_signs = ("="):rep((equal_signs or -1) + 1) + -- Long strings strip out leading newline. We want to retain that, when quoting. + if s:find("^\n") then s = "\n" .. s end + local lbracket, rbracket = + "[" .. equal_signs .. "[", + "]" .. equal_signs .. "]" + s = lbracket .. s .. rbracket + else + -- Escape funny stuff. Lua 5.1 does not handle "\r" correctly. + s = ("%q"):format(s):gsub("\r", "\\r") + end + return s +end + +function stringx.import() + utils.import(stringx,string) +end + +return stringx diff --git a/framework/lualib/thirdparty/pl/tablex.lua b/framework/lualib/thirdparty/pl/tablex.lua new file mode 100755 index 0000000..81f7395 --- /dev/null +++ b/framework/lualib/thirdparty/pl/tablex.lua @@ -0,0 +1,999 @@ +--- Extended operations on Lua tables. +-- +-- See @{02-arrays.md.Useful_Operations_on_Tables|the Guide} +-- +-- Dependencies: `pl.utils`, `pl.types` +-- @module pl.tablex +local utils = require ('pl.utils') +local types = require ('pl.types') +local getmetatable,setmetatable,require = getmetatable,setmetatable,require +local tsort,append,remove = table.sort,table.insert,table.remove +local min = math.min +local pairs,type,unpack,select,tostring = pairs,type,utils.unpack,select,tostring +local function_arg = utils.function_arg +local assert_arg = utils.assert_arg + +local tablex = {} + +-- generally, functions that make copies of tables try to preserve the metatable. +-- However, when the source has no obvious type, then we attach appropriate metatables +-- like List, Map, etc to the result. +local function setmeta (res,tbl,pl_class) + local mt = getmetatable(tbl) or pl_class and require('pl.' .. pl_class) + return mt and setmetatable(res, mt) or res +end + +local function makelist(l) + return setmetatable(l, require('pl.List')) +end + +local function makemap(m) + return setmetatable(m, require('pl.Map')) +end + +local function complain (idx,msg) + error(('argument %d is not %s'):format(idx,msg),3) +end + +local function assert_arg_indexable (idx,val) + if not types.is_indexable(val) then + complain(idx,"indexable") + end +end + +local function assert_arg_iterable (idx,val) + if not types.is_iterable(val) then + complain(idx,"iterable") + end +end + +local function assert_arg_writeable (idx,val) + if not types.is_writeable(val) then + complain(idx,"writeable") + end +end + +--- copy a table into another, in-place. +-- @within Copying +-- @tab t1 destination table +-- @tab t2 source (actually any iterable object) +-- @return first table +function tablex.update (t1,t2) + assert_arg_writeable(1,t1) + assert_arg_iterable(2,t2) + for k,v in pairs(t2) do + t1[k] = v + end + return t1 +end + +--- total number of elements in this table. +-- Note that this is distinct from `#t`, which is the number +-- of values in the array part; this value will always +-- be greater or equal. The difference gives the size of +-- the hash part, for practical purposes. Works for any +-- object with a __pairs metamethod. +-- @tab t a table +-- @return the size +function tablex.size (t) + assert_arg_iterable(1,t) + local i = 0 + for k in pairs(t) do i = i + 1 end + return i +end + +--- make a shallow copy of a table +-- @within Copying +-- @tab t an iterable source +-- @return new table +function tablex.copy (t) + assert_arg_iterable(1,t) + local res = {} + for k,v in pairs(t) do + res[k] = v + end + return res +end + +local function cycle_aware_copy(t, cache) + if type(t) ~= 'table' then return t end + if cache[t] then return cache[t] end + assert_arg_iterable(1,t) + local res = {} + cache[t] = res + local mt = getmetatable(t) + for k,v in pairs(t) do + k = cycle_aware_copy(k, cache) + v = cycle_aware_copy(v, cache) + res[k] = v + end + setmetatable(res,mt) + return res +end + +--- make a deep copy of a table, recursively copying all the keys and fields. +-- This supports cycles in tables; cycles will be reproduced in the copy. +-- This will also set the copied table's metatable to that of the original. +-- @within Copying +-- @tab t A table +-- @return new table +function tablex.deepcopy(t) + return cycle_aware_copy(t,{}) +end + +local abs = math.abs + +local function cycle_aware_compare(t1,t2,ignore_mt,eps,cache) + if cache[t1] and cache[t1][t2] then return true end + local ty1 = type(t1) + local ty2 = type(t2) + if ty1 ~= ty2 then return false end + -- non-table types can be directly compared + if ty1 ~= 'table' then + if ty1 == 'number' and eps then return abs(t1-t2) < eps end + return t1 == t2 + end + -- as well as tables which have the metamethod __eq + local mt = getmetatable(t1) + if not ignore_mt and mt and mt.__eq then return t1 == t2 end + for k1 in pairs(t1) do + if t2[k1]==nil then return false end + end + for k2 in pairs(t2) do + if t1[k2]==nil then return false end + end + cache[t1] = cache[t1] or {} + cache[t1][t2] = true + for k1,v1 in pairs(t1) do + local v2 = t2[k1] + if not cycle_aware_compare(v1,v2,ignore_mt,eps,cache) then return false end + end + return true +end + +--- compare two values. +-- if they are tables, then compare their keys and fields recursively. +-- @within Comparing +-- @param t1 A value +-- @param t2 A value +-- @bool[opt] ignore_mt if true, ignore __eq metamethod (default false) +-- @number[opt] eps if defined, then used for any number comparisons +-- @return true or false +function tablex.deepcompare(t1,t2,ignore_mt,eps) + return cycle_aware_compare(t1,t2,ignore_mt,eps,{}) +end + +--- compare two arrays using a predicate. +-- @within Comparing +-- @array t1 an array +-- @array t2 an array +-- @func cmp A comparison function; `bool = cmp(t1_value, t2_value)` +-- @return true or false +-- @usage +-- assert(tablex.compare({ 1, 2, 3 }, { 1, 2, 3 }, "==")) +-- +-- assert(tablex.compare( +-- {1,2,3, hello = "world"}, -- fields are not compared! +-- {1,2,3}, function(v1, v2) return v1 == v2 end) +function tablex.compare (t1,t2,cmp) + assert_arg_indexable(1,t1) + assert_arg_indexable(2,t2) + if #t1 ~= #t2 then return false end + cmp = function_arg(3,cmp) + for k = 1,#t1 do + if not cmp(t1[k],t2[k]) then return false end + end + return true +end + +--- compare two list-like tables using an optional predicate, without regard for element order. +-- @within Comparing +-- @array t1 a list-like table +-- @array t2 a list-like table +-- @param cmp A comparison function (may be nil) +function tablex.compare_no_order (t1,t2,cmp) + assert_arg_indexable(1,t1) + assert_arg_indexable(2,t2) + if cmp then cmp = function_arg(3,cmp) end + if #t1 ~= #t2 then return false end + local visited = {} + for i = 1,#t1 do + local val = t1[i] + local gotcha + for j = 1,#t2 do + if not visited[j] then + local match + if cmp then match = cmp(val,t2[j]) else match = val == t2[j] end + if match then + gotcha = j + break + end + end + end + if not gotcha then return false end + visited[gotcha] = true + end + return true +end + + +--- return the index of a value in a list. +-- Like string.find, there is an optional index to start searching, +-- which can be negative. +-- @within Finding +-- @array t A list-like table +-- @param val A value +-- @int idx index to start; -1 means last element,etc (default 1) +-- @return index of value or nil if not found +-- @usage find({10,20,30},20) == 2 +-- @usage find({'a','b','a','c'},'a',2) == 3 +function tablex.find(t,val,idx) + assert_arg_indexable(1,t) + idx = idx or 1 + if idx < 0 then idx = #t + idx + 1 end + for i = idx,#t do + if t[i] == val then return i end + end + return nil +end + +--- return the index of a value in a list, searching from the end. +-- Like string.find, there is an optional index to start searching, +-- which can be negative. +-- @within Finding +-- @array t A list-like table +-- @param val A value +-- @param idx index to start; -1 means last element,etc (default `#t`) +-- @return index of value or nil if not found +-- @usage rfind({10,10,10},10) == 3 +function tablex.rfind(t,val,idx) + assert_arg_indexable(1,t) + idx = idx or #t + if idx < 0 then idx = #t + idx + 1 end + for i = idx,1,-1 do + if t[i] == val then return i end + end + return nil +end + + +--- return the index (or key) of a value in a table using a comparison function. +-- +-- *NOTE*: the 2nd return value of this function, the value returned +-- by the comparison function, has a limitation that it cannot be `false`. +-- Because if it is, then it indicates the comparison failed, and the +-- function will continue the search. See examples. +-- @within Finding +-- @tab t A table +-- @func cmp A comparison function +-- @param arg an optional second argument to the function +-- @return index of value, or nil if not found +-- @return value returned by comparison function (cannot be `false`!) +-- @usage +-- -- using an operator +-- local lst = { "Rudolph", true, false, 15 } +-- local idx, cmp_result = tablex.rfind(lst, "==", "Rudolph") +-- assert(idx == 1) +-- assert(cmp_result == true) +-- +-- local idx, cmp_result = tablex.rfind(lst, "==", false) +-- assert(idx == 3) +-- assert(cmp_result == true) -- looking up 'false' works! +-- +-- -- using a function returning the value looked up +-- local cmp = function(v1, v2) return v1 == v2 and v2 end +-- local idx, cmp_result = tablex.rfind(lst, cmp, "Rudolph") +-- assert(idx == 1) +-- assert(cmp_result == "Rudolph") -- the value is returned +-- +-- -- NOTE: this fails, since 'false' cannot be returned! +-- local idx, cmp_result = tablex.rfind(lst, cmp, false) +-- assert(idx == nil) -- looking up 'false' failed! +-- assert(cmp_result == nil) +function tablex.find_if(t,cmp,arg) + assert_arg_iterable(1,t) + cmp = function_arg(2,cmp) + for k,v in pairs(t) do + local c = cmp(v,arg) + if c then return k,c end + end + return nil +end + +--- return a list of all values in a table indexed by another list. +-- @tab tbl a table +-- @array idx an index table (a list of keys) +-- @return a list-like table +-- @usage index_by({10,20,30,40},{2,4}) == {20,40} +-- @usage index_by({one=1,two=2,three=3},{'one','three'}) == {1,3} +function tablex.index_by(tbl,idx) + assert_arg_indexable(1,tbl) + assert_arg_indexable(2,idx) + local res = {} + for i = 1,#idx do + res[i] = tbl[idx[i]] + end + return setmeta(res,tbl,'List') +end + +--- apply a function to all values of a table. +-- This returns a table of the results. +-- Any extra arguments are passed to the function. +-- @within MappingAndFiltering +-- @func fun A function that takes at least one argument +-- @tab t A table +-- @param ... optional arguments +-- @usage map(function(v) return v*v end, {10,20,30,fred=2}) is {100,400,900,fred=4} +function tablex.map(fun,t,...) + assert_arg_iterable(1,t) + fun = function_arg(1,fun) + local res = {} + for k,v in pairs(t) do + res[k] = fun(v,...) + end + return setmeta(res,t) +end + +--- apply a function to all values of a list. +-- This returns a table of the results. +-- Any extra arguments are passed to the function. +-- @within MappingAndFiltering +-- @func fun A function that takes at least one argument +-- @array t a table (applies to array part) +-- @param ... optional arguments +-- @return a list-like table +-- @usage imap(function(v) return v*v end, {10,20,30,fred=2}) is {100,400,900} +function tablex.imap(fun,t,...) + assert_arg_indexable(1,t) + fun = function_arg(1,fun) + local res = {} + for i = 1,#t do + res[i] = fun(t[i],...) or false + end + return setmeta(res,t,'List') +end + +--- apply a named method to values from a table. +-- @within MappingAndFiltering +-- @string name the method name +-- @array t a list-like table +-- @param ... any extra arguments to the method +-- @return a `List` with the results of the method (1st result only) +-- @usage +-- local Car = {} +-- Car.__index = Car +-- function Car.new(car) +-- return setmetatable(car or {}, Car) +-- end +-- Car.speed = 0 +-- function Car:faster(increase) +-- self.speed = self.speed + increase +-- return self.speed +-- end +-- +-- local ferrari = Car.new{ name = "Ferrari" } +-- local lamborghini = Car.new{ name = "Lamborghini", speed = 50 } +-- local cars = { ferrari, lamborghini } +-- +-- assert(ferrari.speed == 0) +-- assert(lamborghini.speed == 50) +-- tablex.map_named_method("faster", cars, 10) +-- assert(ferrari.speed == 10) +-- assert(lamborghini.speed == 60) +function tablex.map_named_method (name,t,...) + utils.assert_string(1,name) + assert_arg_indexable(2,t) + local res = {} + for i = 1,#t do + local val = t[i] + local fun = val[name] + res[i] = fun(val,...) + end + return setmeta(res,t,'List') +end + +--- apply a function to all values of a table, in-place. +-- Any extra arguments are passed to the function. +-- @func fun A function that takes at least one argument +-- @tab t a table +-- @param ... extra arguments passed to `fun` +-- @see tablex.foreach +function tablex.transform (fun,t,...) + assert_arg_iterable(1,t) + fun = function_arg(1,fun) + for k,v in pairs(t) do + t[k] = fun(v,...) + end +end + +--- generate a table of all numbers in a range. +-- This is consistent with a numerical for loop. +-- @int start number +-- @int finish number +-- @int[opt=1] step make this negative for start < finish +function tablex.range (start,finish,step) + local res + step = step or 1 + if start == finish then + res = {start} + elseif (start > finish and step > 0) or (finish > start and step < 0) then + res = {} + else + local k = 1 + res = {} + for i=start,finish,step do res[k]=i; k=k+1 end + end + return makelist(res) +end + +--- apply a function to values from two tables. +-- @within MappingAndFiltering +-- @func fun a function of at least two arguments +-- @tab t1 a table +-- @tab t2 a table +-- @param ... extra arguments +-- @return a table +-- @usage map2('+',{1,2,3,m=4},{10,20,30,m=40}) is {11,22,23,m=44} +function tablex.map2 (fun,t1,t2,...) + assert_arg_iterable(1,t1) + assert_arg_iterable(2,t2) + fun = function_arg(1,fun) + local res = {} + for k,v in pairs(t1) do + res[k] = fun(v,t2[k],...) + end + return setmeta(res,t1,'List') +end + +--- apply a function to values from two arrays. +-- The result will be the length of the shortest array. +-- @within MappingAndFiltering +-- @func fun a function of at least two arguments +-- @array t1 a list-like table +-- @array t2 a list-like table +-- @param ... extra arguments +-- @usage imap2('+',{1,2,3,m=4},{10,20,30,m=40}) is {11,22,23} +function tablex.imap2 (fun,t1,t2,...) + assert_arg_indexable(2,t1) + assert_arg_indexable(3,t2) + fun = function_arg(1,fun) + local res,n = {},math.min(#t1,#t2) + for i = 1,n do + res[i] = fun(t1[i],t2[i],...) + end + return res +end + +--- 'reduce' a list using a binary function. +-- @func fun a function of two arguments +-- @array t a list-like table +-- @array memo optional initial memo value. Defaults to first value in table. +-- @return the result of the function +-- @usage reduce('+',{1,2,3,4}) == 10 +function tablex.reduce (fun,t,memo) + assert_arg_indexable(2,t) + fun = function_arg(1,fun) + local n = #t + if n == 0 then + return memo + end + local res = memo and fun(memo, t[1]) or t[1] + for i = 2,n do + res = fun(res,t[i]) + end + return res +end + +--- apply a function to all elements of a table. +-- The arguments to the function will be the value, +-- the key and _finally_ any extra arguments passed to this function. +-- Note that the Lua 5.0 function table.foreach passed the _key_ first. +-- @within Iterating +-- @tab t a table +-- @func fun a function on the elements; `function(value, key, ...)` +-- @param ... extra arguments passed to `fun` +-- @see tablex.transform +function tablex.foreach(t,fun,...) + assert_arg_iterable(1,t) + fun = function_arg(2,fun) + for k,v in pairs(t) do + fun(v,k,...) + end +end + +--- apply a function to all elements of a list-like table in order. +-- The arguments to the function will be the value, +-- the index and _finally_ any extra arguments passed to this function +-- @within Iterating +-- @array t a table +-- @func fun a function with at least one argument +-- @param ... optional arguments +function tablex.foreachi(t,fun,...) + assert_arg_indexable(1,t) + fun = function_arg(2,fun) + for i = 1,#t do + fun(t[i],i,...) + end +end + +--- Apply a function to a number of tables. +-- A more general version of map +-- The result is a table containing the result of applying that function to the +-- ith value of each table. Length of output list is the minimum length of all the lists +-- @within MappingAndFiltering +-- @func fun a function of n arguments +-- @tab ... n tables +-- @usage mapn(function(x,y,z) return x+y+z end, {1,2,3},{10,20,30},{100,200,300}) is {111,222,333} +-- @usage mapn(math.max, {1,20,300},{10,2,3},{100,200,100}) is {100,200,300} +-- @param fun A function that takes as many arguments as there are tables +function tablex.mapn(fun,...) + fun = function_arg(1,fun) + local res = {} + local lists = {...} + local minn = 1e40 + for i = 1,#lists do + minn = min(minn,#(lists[i])) + end + for i = 1,minn do + local args,k = {},1 + for j = 1,#lists do + args[k] = lists[j][i] + k = k + 1 + end + res[#res+1] = fun(unpack(args)) + end + return res +end + +--- call the function with the key and value pairs from a table. +-- The function can return a value and a key (note the order!). If both +-- are not nil, then this pair is inserted into the result: if the key already exists, we convert the value for that +-- key into a table and append into it. If only value is not nil, then it is appended to the result. +-- @within MappingAndFiltering +-- @func fun A function which will be passed each key and value as arguments, plus any extra arguments to pairmap. +-- @tab t A table +-- @param ... optional arguments +-- @usage pairmap(function(k,v) return v end,{fred=10,bonzo=20}) is {10,20} _or_ {20,10} +-- @usage pairmap(function(k,v) return {k,v},k end,{one=1,two=2}) is {one={'one',1},two={'two',2}} +function tablex.pairmap(fun,t,...) + assert_arg_iterable(1,t) + fun = function_arg(1,fun) + local res = {} + for k,v in pairs(t) do + local rv,rk = fun(k,v,...) + if rk then + if res[rk] then + if type(res[rk]) == 'table' then + table.insert(res[rk],rv) + else + res[rk] = {res[rk], rv} + end + else + res[rk] = rv + end + else + res[#res+1] = rv + end + end + return res +end + +local function keys_op(i,v) return i end + +--- return all the keys of a table in arbitrary order. +-- @within Extraction +-- @tab t A table +function tablex.keys(t) + assert_arg_iterable(1,t) + return makelist(tablex.pairmap(keys_op,t)) +end + +local function values_op(i,v) return v end + +--- return all the values of the table in arbitrary order +-- @within Extraction +-- @tab t A table +function tablex.values(t) + assert_arg_iterable(1,t) + return makelist(tablex.pairmap(values_op,t)) +end + +local function index_map_op (i,v) return i,v end + +--- create an index map from a list-like table. The original values become keys, +-- and the associated values are the indices into the original list. +-- @array t a list-like table +-- @return a map-like table +function tablex.index_map (t) + assert_arg_indexable(1,t) + return makemap(tablex.pairmap(index_map_op,t)) +end + +local function set_op(i,v) return true,v end + +--- create a set from a list-like table. A set is a table where the original values +-- become keys, and the associated values are all true. +-- @array t a list-like table +-- @return a set (a map-like table) +function tablex.makeset (t) + assert_arg_indexable(1,t) + return setmetatable(tablex.pairmap(set_op,t),require('pl.Set')) +end + +--- combine two tables, either as union or intersection. Corresponds to +-- set operations for sets () but more general. Not particularly +-- useful for list-like tables. +-- @within Merging +-- @tab t1 a table +-- @tab t2 a table +-- @bool dup true for a union, false for an intersection. +-- @usage merge({alice=23,fred=34},{bob=25,fred=34}) is {fred=34} +-- @usage merge({alice=23,fred=34},{bob=25,fred=34},true) is {bob=25,fred=34,alice=23} +-- @see tablex.index_map +function tablex.merge (t1,t2,dup) + assert_arg_iterable(1,t1) + assert_arg_iterable(2,t2) + local res = {} + for k,v in pairs(t1) do + if dup or t2[k] then res[k] = v end + end + if dup then + for k,v in pairs(t2) do + res[k] = v + end + end + return setmeta(res,t1,'Map') +end + +--- the union of two map-like tables. +-- If there are duplicate keys, the second table wins. +-- @tab t1 a table +-- @tab t2 a table +-- @treturn tab +-- @see tablex.merge +function tablex.union(t1, t2) + return tablex.merge(t1, t2, true) +end + +--- the intersection of two map-like tables. +-- @tab t1 a table +-- @tab t2 a table +-- @treturn tab +-- @see tablex.merge +function tablex.intersection(t1, t2) + return tablex.merge(t1, t2, false) +end + +--- a new table which is the difference of two tables. +-- With sets (where the values are all true) this is set difference and +-- symmetric difference depending on the third parameter. +-- @within Merging +-- @tab s1 a map-like table or set +-- @tab s2 a map-like table or set +-- @bool symm symmetric difference (default false) +-- @return a map-like table or set +function tablex.difference (s1,s2,symm) + assert_arg_iterable(1,s1) + assert_arg_iterable(2,s2) + local res = {} + for k,v in pairs(s1) do + if s2[k] == nil then res[k] = v end + end + if symm then + for k,v in pairs(s2) do + if s1[k] == nil then res[k] = v end + end + end + return setmeta(res,s1,'Map') +end + +--- A table where the key/values are the values and value counts of the table. +-- @array t a list-like table +-- @func cmp a function that defines equality (otherwise uses ==) +-- @return a map-like table +-- @see seq.count_map +function tablex.count_map (t,cmp) + assert_arg_indexable(1,t) + local res,mask = {},{} + cmp = function_arg(2,cmp or '==') + local n = #t + for i = 1,#t do + local v = t[i] + if not mask[v] then + mask[v] = true + -- check this value against all other values + res[v] = 1 -- there's at least one instance + for j = i+1,n do + local w = t[j] + local ok = cmp(v,w) + if ok then + res[v] = res[v] + 1 + mask[w] = true + end + end + end + end + return makemap(res) +end + +--- filter an array's values using a predicate function +-- @within MappingAndFiltering +-- @array t a list-like table +-- @func pred a boolean function +-- @param arg optional argument to be passed as second argument of the predicate +function tablex.filter (t,pred,arg) + assert_arg_indexable(1,t) + pred = function_arg(2,pred) + local res,k = {},1 + for i = 1,#t do + local v = t[i] + if pred(v,arg) then + res[k] = v + k = k + 1 + end + end + return setmeta(res,t,'List') +end + +--- return a table where each element is a table of the ith values of an arbitrary +-- number of tables. It is equivalent to a matrix transpose. +-- @within Merging +-- @usage zip({10,20,30},{100,200,300}) is {{10,100},{20,200},{30,300}} +-- @array ... arrays to be zipped +function tablex.zip(...) + return tablex.mapn(function(...) return {...} end,...) +end + +local _copy +function _copy (dest,src,idest,isrc,nsrc,clean_tail) + idest = idest or 1 + isrc = isrc or 1 + local iend + if not nsrc then + nsrc = #src + iend = #src + else + iend = isrc + min(nsrc-1,#src-isrc) + end + if dest == src then -- special case + if idest > isrc and iend >= idest then -- overlapping ranges + src = tablex.sub(src,isrc,nsrc) + isrc = 1; iend = #src + end + end + for i = isrc,iend do + dest[idest] = src[i] + idest = idest + 1 + end + if clean_tail then + tablex.clear(dest,idest) + end + return dest +end + +--- copy an array into another one, clearing `dest` after `idest+nsrc`, if necessary. +-- @within Copying +-- @array dest a list-like table +-- @array src a list-like table +-- @int[opt=1] idest where to start copying values into destination +-- @int[opt=1] isrc where to start copying values from source +-- @int[opt=#src] nsrc number of elements to copy from source +function tablex.icopy (dest,src,idest,isrc,nsrc) + assert_arg_indexable(1,dest) + assert_arg_indexable(2,src) + return _copy(dest,src,idest,isrc,nsrc,true) +end + +--- copy an array into another one. +-- @within Copying +-- @array dest a list-like table +-- @array src a list-like table +-- @int[opt=1] idest where to start copying values into destination +-- @int[opt=1] isrc where to start copying values from source +-- @int[opt=#src] nsrc number of elements to copy from source +function tablex.move (dest,src,idest,isrc,nsrc) + assert_arg_indexable(1,dest) + assert_arg_indexable(2,src) + return _copy(dest,src,idest,isrc,nsrc,false) +end + +function tablex._normalize_slice(self,first,last) + local sz = #self + if not first then first=1 end + if first<0 then first=sz+first+1 end + -- make the range _inclusive_! + if not last then last=sz end + if last < 0 then last=sz+1+last end + return first,last +end + +--- Extract a range from a table, like 'string.sub'. +-- If first or last are negative then they are relative to the end of the list +-- eg. sub(t,-2) gives last 2 entries in a list, and +-- sub(t,-4,-2) gives from -4th to -2nd +-- @within Extraction +-- @array t a list-like table +-- @int first An index +-- @int last An index +-- @return a new List +function tablex.sub(t,first,last) + assert_arg_indexable(1,t) + first,last = tablex._normalize_slice(t,first,last) + local res={} + for i=first,last do append(res,t[i]) end + return setmeta(res,t,'List') +end + +--- set an array range to a value. If it's a function we use the result +-- of applying it to the indices. +-- @array t a list-like table +-- @param val a value +-- @int[opt=1] i1 start range +-- @int[opt=#t] i2 end range +function tablex.set (t,val,i1,i2) + assert_arg_indexable(1,t) + i1,i2 = i1 or 1,i2 or #t + if types.is_callable(val) then + for i = i1,i2 do + t[i] = val(i) + end + else + for i = i1,i2 do + t[i] = val + end + end +end + +--- create a new array of specified size with initial value. +-- @int n size +-- @param val initial value (can be `nil`, but don't expect `#` to work!) +-- @return the table +function tablex.new (n,val) + local res = {} + tablex.set(res,val,1,n) + return res +end + +--- clear out the contents of a table. +-- @array t a list +-- @param istart optional start position +function tablex.clear(t,istart) + istart = istart or 1 + for i = istart,#t do remove(t) end +end + +--- insert values into a table. +-- similar to `table.insert` but inserts values from given table `values`, +-- not the object itself, into table `t` at position `pos`. +-- @within Copying +-- @array t the list +-- @int[opt] position (default is at end) +-- @array values +function tablex.insertvalues(t, ...) + assert_arg(1,t,'table') + local pos, values + if select('#', ...) == 1 then + pos,values = #t+1, ... + else + pos,values = ... + end + if #values > 0 then + for i=#t,pos,-1 do + t[i+#values] = t[i] + end + local offset = 1 - pos + for i=pos,pos+#values-1 do + t[i] = values[i + offset] + end + end + return t +end + +--- remove a range of values from a table. +-- End of range may be negative. +-- @array t a list-like table +-- @int i1 start index +-- @int i2 end index +-- @return the table +function tablex.removevalues (t,i1,i2) + assert_arg(1,t,'table') + i1,i2 = tablex._normalize_slice(t,i1,i2) + for i = i1,i2 do + remove(t,i1) + end + return t +end + +local _find +_find = function (t,value,tables) + for k,v in pairs(t) do + if v == value then return k end + end + for k,v in pairs(t) do + if not tables[v] and type(v) == 'table' then + tables[v] = true + local res = _find(v,value,tables) + if res then + res = tostring(res) + if type(k) ~= 'string' then + return '['..k..']'..res + else + return k..'.'..res + end + end + end + end +end + +--- find a value in a table by recursive search. +-- @within Finding +-- @tab t the table +-- @param value the value +-- @array[opt] exclude any tables to avoid searching +-- @return a fieldspec, e.g. 'a.b' or 'math.sin' +-- @usage search(_G,math.sin,{package.path}) == 'math.sin' +function tablex.search (t,value,exclude) + assert_arg_iterable(1,t) + local tables = {[t]=true} + if exclude then + for _,v in pairs(exclude) do tables[v] = true end + end + return _find(t,value,tables) +end + +--- return an iterator to a table sorted by its keys +-- @within Iterating +-- @tab t the table +-- @func f an optional comparison function (f(x,y) is true if x < y) +-- @usage for k,v in tablex.sort(t) do print(k,v) end +-- @return an iterator to traverse elements sorted by the keys +function tablex.sort(t,f) + local keys = {} + for k in pairs(t) do keys[#keys + 1] = k end + tsort(keys,f) + local i = 0 + return function() + i = i + 1 + return keys[i], t[keys[i]] + end +end + +--- return an iterator to a table sorted by its values +-- @within Iterating +-- @tab t the table +-- @func f an optional comparison function (f(x,y) is true if x < y) +-- @usage for k,v in tablex.sortv(t) do print(k,v) end +-- @return an iterator to traverse elements sorted by the values +function tablex.sortv(t,f) + f = function_arg(2, f or '<') + local keys = {} + for k in pairs(t) do keys[#keys + 1] = k end + tsort(keys,function(x, y) return f(t[x], t[y]) end) + local i = 0 + return function() + i = i + 1 + return keys[i], t[keys[i]] + end +end + +--- modifies a table to be read only. +-- This only offers weak protection. Tables can still be modified with +-- `table.insert` and `rawset`. +-- +-- *NOTE*: for Lua 5.1 length, pairs and ipairs will not work, since the +-- equivalent metamethods are only available in Lua 5.2 and newer. +-- @tab t the table +-- @return the table read only (a proxy). +function tablex.readonly(t) + local mt = { + __index=t, + __newindex=function(t, k, v) error("Attempt to modify read-only table", 2) end, + __pairs=function() return pairs(t) end, + __ipairs=function() return ipairs(t) end, + __len=function() return #t end, + __metatable=false + } + return setmetatable({}, mt) +end + +return tablex diff --git a/framework/lualib/thirdparty/pl/template.lua b/framework/lualib/thirdparty/pl/template.lua new file mode 100755 index 0000000..9961a1b --- /dev/null +++ b/framework/lualib/thirdparty/pl/template.lua @@ -0,0 +1,202 @@ +--- A template preprocessor. +-- Originally by [Ricki Lake](http://lua-users.org/wiki/SlightlyLessSimpleLuaPreprocessor) +-- +-- There are two rules: +-- +-- * lines starting with # are Lua +-- * otherwise, `$(expr)` is the result of evaluating `expr` +-- +-- Example: +-- +-- # for i = 1,3 do +-- $(i) Hello, Word! +-- # end +-- ===> +-- 1 Hello, Word! +-- 2 Hello, Word! +-- 3 Hello, Word! +-- +-- Other escape characters can be used, when the defaults conflict +-- with the output language. +-- +-- > for _,n in pairs{'one','two','three'} do +-- static int l_${n} (luaState *state); +-- > end +-- +-- See @{03-strings.md.Another_Style_of_Template|the Guide}. +-- +-- Dependencies: `pl.utils` +-- @module pl.template + +local utils = require 'pl.utils' + +local append,format,strsub,strfind,strgsub = table.insert,string.format,string.sub,string.find,string.gsub + +local APPENDER = "\n__R_size = __R_size + 1; __R_table[__R_size] = " + +local function parseDollarParen(pieces, chunk, exec_pat, newline) + local s = 1 + for term, executed, e in chunk:gmatch(exec_pat) do + executed = '('..strsub(executed,2,-2)..')' + append(pieces, APPENDER..format("%q", strsub(chunk,s, term - 1))) + append(pieces, APPENDER..format("__tostring(%s or '')", executed)) + s = e + end + local r + if newline then + r = format("%q", strgsub(strsub(chunk,s),"\n","")) + else + r = format("%q", strsub(chunk,s)) + end + if r ~= '""' then + append(pieces, APPENDER..r) + end +end + +local function parseHashLines(chunk,inline_escape,brackets,esc,newline) + local exec_pat = "()"..inline_escape.."(%b"..brackets..")()" + + local esc_pat = esc.."+([^\n]*\n?)" + local esc_pat1, esc_pat2 = "^"..esc_pat, "\n"..esc_pat + local pieces, s = {"return function()\nlocal __R_size, __R_table, __tostring = 0, {}, __tostring", n = 1}, 1 + while true do + local _, e, lua = strfind(chunk,esc_pat1, s) + if not e then + local ss + ss, e, lua = strfind(chunk,esc_pat2, s) + parseDollarParen(pieces, strsub(chunk,s, ss), exec_pat, newline) + if not e then break end + end + if strsub(lua, -1, -1) == "\n" then lua = strsub(lua, 1, -2) end + append(pieces, "\n"..lua) + s = e + 1 + end + append(pieces, "\nreturn __R_table\nend") + + -- let's check for a special case where there is nothing to template, but it's + -- just a single static string + local short = false + if (#pieces == 3) and (pieces[2]:find(APPENDER, 1, true) == 1) then + pieces = { "return " .. pieces[2]:sub(#APPENDER+1,-1) } + short = true + end + -- if short == true, the generated function will not return a table of strings, + -- but a single string + return table.concat(pieces), short +end + +local template = {} + +--- expand the template using the specified environment. +-- This function will compile and render the template. For more performant +-- recurring usage use the two step approach by using `compile` and `ct:render`. +-- There are six special fields in the environment table `env` +-- +-- * `_parent`: continue looking up in this table (e.g. `_parent=_G`). +-- * `_brackets`: bracket pair that wraps inline Lua expressions, default is '()'. +-- * `_escape`: character marking Lua lines, default is '#' +-- * `_inline_escape`: character marking inline Lua expression, default is '$'. +-- * `_chunk_name`: chunk name for loaded templates, used if there +-- is an error in Lua code. Default is 'TMP'. +-- * `_debug`: if truthy, the generated code will be printed upon a render error +-- +-- @string str the template string +-- @tab[opt] env the environment +-- @return `rendered template + nil + source_code`, or `nil + error + source_code`. The last +-- return value (`source_code`) is only returned if the debug option is used. +function template.substitute(str,env) + env = env or {} + local t, err = template.compile(str, { + chunk_name = rawget(env,"_chunk_name"), + escape = rawget(env,"_escape"), + inline_escape = rawget(env,"_inline_escape"), + inline_brackets = rawget(env,"_brackets"), + newline = nil, + debug = rawget(env,"_debug") + }) + if not t then return t, err end + + return t:render(env, rawget(env,"_parent"), rawget(env,"_debug")) +end + +--- executes the previously compiled template and renders it. +-- @function ct:render +-- @tab[opt] env the environment. +-- @tab[opt] parent continue looking up in this table (e.g. `parent=_G`). +-- @bool[opt] db if thruthy, it will print the code upon a render error +-- (provided the template was compiled with the debug option). +-- @return `rendered template + nil + source_code`, or `nil + error + source_code`. The last return value +-- (`source_code`) is only returned if the template was compiled with the debug option. +-- @usage +-- local ct, err = template.compile(my_template) +-- local rendered , err = ct:render(my_env, parent) +local render = function(self, env, parent, db) + env = env or {} + if parent then -- parent is a bit silly, but for backward compatibility retained + setmetatable(env, {__index = parent}) + end + setmetatable(self.env, {__index = env}) + + local res, out = xpcall(self.fn, debug.traceback) + if not res then + if self.code and db then print(self.code) end + return nil, out, self.code + end + return table.concat(out), nil, self.code +end + +--- compiles the template. +-- Returns an object that can repeatedly be rendered without parsing/compiling +-- the template again. +-- The options passed in the `opts` table support the following options: +-- +-- * `chunk_name`: chunk name for loaded templates, used if there +-- is an error in Lua code. Default is 'TMP'. +-- * `escape`: character marking Lua lines, default is '#' +-- * `inline_escape`: character marking inline Lua expression, default is '$'. +-- * `inline_brackets`: bracket pair that wraps inline Lua expressions, default is '()'. +-- * `newline`: string to replace newline characters, default is `nil` (not replacing newlines). +-- * `debug`: if truthy, the generated source code will be retained within the compiled template object, default is `nil`. +-- +-- @string str the template string +-- @tab[opt] opts the compilation options to use +-- @return template object, or `nil + error + source_code` +-- @usage +-- local ct, err = template.compile(my_template) +-- local rendered , err = ct:render(my_env, parent) +function template.compile(str, opts) + opts = opts or {} + local chunk_name = opts.chunk_name or 'TMP' + local escape = opts.escape or '#' + local inline_escape = opts.inline_escape or '$' + local inline_brackets = opts.inline_brackets or '()' + + local code, short = parseHashLines(str,inline_escape,inline_brackets,escape,opts.newline) + local env = { __tostring = tostring } + local fn, err = utils.load(code, chunk_name,'t',env) + if not fn then return nil, err, code end + + if short then + -- the template returns a single constant string, let's optimize for that + local constant_string = fn() + return { + fn = fn(), + env = env, + render = function(self) -- additional params can be ignored + -- skip the metatable magic and error handling in the render + -- function above for this special case + return constant_string, nil, self.code + end, + code = opts.debug and code or nil, + } + end + + return { + fn = fn(), + env = env, + render = render, + code = opts.debug and code or nil, + } +end + +return template diff --git a/framework/lualib/thirdparty/pl/test.lua b/framework/lualib/thirdparty/pl/test.lua new file mode 100755 index 0000000..694bbc5 --- /dev/null +++ b/framework/lualib/thirdparty/pl/test.lua @@ -0,0 +1,164 @@ +--- Useful test utilities. +-- +-- test.asserteq({1,2},{1,2}) -- can compare tables +-- test.asserteq(1.2,1.19,0.02) -- compare FP numbers within precision +-- T = test.tuple -- used for comparing multiple results +-- test.asserteq(T(string.find(" me","me")),T(2,3)) +-- +-- Dependencies: `pl.utils`, `pl.tablex`, `pl.pretty`, `pl.path`, `debug` +-- @module pl.test + +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local pretty = require 'pl.pretty' +local path = require 'pl.path' +local type,unpack,pack = type,utils.unpack,utils.pack +local clock = os.clock +local debug = require 'debug' +local io = io + +local function dump(x) + if type(x) == 'table' and not (getmetatable(x) and getmetatable(x).__tostring) then + return pretty.write(x,' ',true) + elseif type(x) == 'string' then + return '"'..x..'"' + else + return tostring(x) + end +end + +local test = {} + +---- error handling for test results. +-- By default, this writes to stderr and exits the program. +-- Re-define this function to raise an error and/or redirect output +function test.error_handler(file,line,got_text, needed_text,msg) + local err = io.stderr + err:write(path.basename(file)..':'..line..': assertion failed\n') + err:write("got:\t",got_text,'\n') + err:write("needed:\t",needed_text,'\n') + utils.quit(1,msg or "these values were not equal") +end + +local function complain (x,y,msg,where) + local i = debug.getinfo(3 + (where or 0)) + test.error_handler(i.short_src,i.currentline,dump(x),dump(y),msg) +end + +--- general test complain message. +-- Useful for composing new test functions (see tests/tablex.lua for an example) +-- @param x a value +-- @param y value to compare first value against +-- @param msg message +-- @param where extra level offset for errors +-- @function complain +test.complain = complain + +--- like assert, except takes two arguments that must be equal and can be tables. +-- If they are plain tables, it will use tablex.deepcompare. +-- @param x any value +-- @param y a value equal to x +-- @param eps an optional tolerance for numerical comparisons +-- @param where extra level offset +function test.asserteq (x,y,eps,where) + local res = x == y + if not res then + res = tablex.deepcompare(x,y,true,eps) + end + if not res then + complain(x,y,nil,where) + end +end + +--- assert that the first string matches the second. +-- @param s1 a string +-- @param s2 a string +-- @param where extra level offset +function test.assertmatch (s1,s2,where) + if not s1:match(s2) then + complain (s1,s2,"these strings did not match",where) + end +end + +--- assert that the function raises a particular error. +-- @param fn a function or a table of the form {function,arg1,...} +-- @param e a string to match the error against +-- @param where extra level offset +function test.assertraise(fn,e,where) + local ok, err + if type(fn) == 'table' then + ok, err = pcall(unpack(fn)) + else + ok, err = pcall(fn) + end + if ok or err:match(e)==nil then + complain (err,e,"these errors did not match",where) + end +end + +--- a version of asserteq that takes two pairs of values. +-- x1==y1 and x2==y2 must be true. Useful for functions that naturally +-- return two values. +-- @param x1 any value +-- @param x2 any value +-- @param y1 any value +-- @param y2 any value +-- @param where extra level offset +function test.asserteq2 (x1,x2,y1,y2,where) + if x1 ~= y1 then complain(x1,y1,nil,where) end + if x2 ~= y2 then complain(x2,y2,nil,where) end +end + +-- tuple type -- + +local tuple_mt = { + unpack = unpack +} +tuple_mt.__index = tuple_mt + +function tuple_mt.__tostring(self) + local ts = {} + for i=1, self.n do + local s = self[i] + ts[i] = type(s) == 'string' and ('%q'):format(s) or tostring(s) + end + return 'tuple(' .. table.concat(ts, ', ') .. ')' +end + +function tuple_mt.__eq(a, b) + if a.n ~= b.n then return false end + for i=1, a.n do + if a[i] ~= b[i] then return false end + end + return true +end + +function tuple_mt.__len(self) + return self.n +end + +--- encode an arbitrary argument list as a tuple. +-- This can be used to compare to other argument lists, which is +-- very useful for testing functions which return a number of values. +-- Unlike regular array-like tables ('sequences') they may contain nils. +-- Tuples understand equality and know how to print themselves out. +-- The # operator is defined to be the size, irrespecive of any nils, +-- and there is an `unpack` method. +-- @usage asserteq(tuple( ('ab'):find 'a'), tuple(1,1)) +function test.tuple(...) + return setmetatable(pack(...), tuple_mt) +end + +--- Time a function. Call the function a given number of times, and report the number of seconds taken, +-- together with a message. Any extra arguments will be passed to the function. +-- @string msg a descriptive message +-- @int n number of times to call the function +-- @func fun the function +-- @param ... optional arguments to fun +function test.timer(msg,n,fun,...) + local start = clock() + for i = 1,n do fun(...) end + utils.printf("%s: took %7.2f sec\n",msg,clock()-start) +end + +return test diff --git a/framework/lualib/thirdparty/pl/text.lua b/framework/lualib/thirdparty/pl/text.lua new file mode 100755 index 0000000..e626e86 --- /dev/null +++ b/framework/lualib/thirdparty/pl/text.lua @@ -0,0 +1,246 @@ +--- Text processing utilities. +-- +-- This provides a Template class (modeled after the same from the Python +-- libraries, see string.Template). It also provides similar functions to those +-- found in the textwrap module. +-- +-- See @{03-strings.md.String_Templates|the Guide}. +-- +-- Calling `text.format_operator()` overloads the % operator for strings to give Python/Ruby style formated output. +-- This is extended to also do template-like substitution for map-like data. +-- +-- > require 'pl.text'.format_operator() +-- > = '%s = %5.3f' % {'PI',math.pi} +-- PI = 3.142 +-- > = '$name = $value' % {name='dog',value='Pluto'} +-- dog = Pluto +-- +-- Dependencies: `pl.utils`, `pl.types` +-- @module pl.text + +local gsub = string.gsub +local concat,append = table.concat,table.insert +local utils = require 'pl.utils' +local bind1,usplit,assert_arg = utils.bind1,utils.split,utils.assert_arg +local is_callable = require 'pl.types'.is_callable +local unpack = utils.unpack + +local text = {} + + +local function makelist(l) + return setmetatable(l, require('pl.List')) +end + +local function lstrip(str) return (str:gsub('^%s+','')) end +local function strip(str) return (lstrip(str):gsub('%s+$','')) end +local function split(s,delim) return makelist(usplit(s,delim)) end + +local function imap(f,t,...) + local res = {} + for i = 1,#t do res[i] = f(t[i],...) end + return res +end + +local function _indent (s,sp) + local sl = split(s,'\n') + return concat(imap(bind1('..',sp),sl),'\n')..'\n' +end + +--- indent a multiline string. +-- @param s the string +-- @param n the size of the indent +-- @param ch the character to use when indenting (default ' ') +-- @return indented string +function text.indent (s,n,ch) + assert_arg(1,s,'string') + assert_arg(2,n,'number') + return _indent(s,string.rep(ch or ' ',n)) +end + +--- dedent a multiline string by removing any initial indent. +-- useful when working with [[..]] strings. +-- @param s the string +-- @return a string with initial indent zero. +function text.dedent (s) + assert_arg(1,s,'string') + local sl = split(s,'\n') + local _,i2 = (#sl>0 and sl[1] or ''):find('^%s*') + sl = imap(string.sub,sl,i2+1) + return concat(sl,'\n')..'\n' +end + +--- format a paragraph into lines so that they fit into a line width. +-- It will not break long words, so lines can be over the length +-- to that extent. +-- @param s the string +-- @param width the margin width, default 70 +-- @return a list of lines (List object) +-- @see pl.List +function text.wrap (s,width) + assert_arg(1,s,'string') + width = width or 70 + s = s:gsub('\n',' ') + local i,nxt = 1 + local lines,line = {} + while i < #s do + nxt = i+width + if s:find("[%w']",nxt) then -- inside a word + nxt = s:find('%W',nxt+1) -- so find word boundary + end + line = s:sub(i,nxt) + i = i + #line + append(lines,strip(line)) + end + return makelist(lines) +end + +--- format a paragraph so that it fits into a line width. +-- @param s the string +-- @param width the margin width, default 70 +-- @return a string +-- @see wrap +function text.fill (s,width) + return concat(text.wrap(s,width),'\n') .. '\n' +end + +local Template = {} +text.Template = Template +Template.__index = Template +setmetatable(Template, { + __call = function(obj,tmpl) + return Template.new(tmpl) + end}) + +function Template.new(tmpl) + assert_arg(1,tmpl,'string') + local res = {} + res.tmpl = tmpl + setmetatable(res,Template) + return res +end + +local function _substitute(s,tbl,safe) + local subst + if is_callable(tbl) then + subst = tbl + else + function subst(f) + local s = tbl[f] + if not s then + if safe then + return f + else + error("not present in table "..f) + end + else + return s + end + end + end + local res = gsub(s,'%${([%w_]+)}',subst) + return (gsub(res,'%$([%w_]+)',subst)) +end + +--- substitute values into a template, throwing an error. +-- This will throw an error if no name is found. +-- @param tbl a table of name-value pairs. +function Template:substitute(tbl) + assert_arg(1,tbl,'table') + return _substitute(self.tmpl,tbl,false) +end + +--- substitute values into a template. +-- This version just passes unknown names through. +-- @param tbl a table of name-value pairs. +function Template:safe_substitute(tbl) + assert_arg(1,tbl,'table') + return _substitute(self.tmpl,tbl,true) +end + +--- substitute values into a template, preserving indentation.
+-- If the value is a multiline string _or_ a template, it will insert +-- the lines at the correct indentation.
+-- Furthermore, if a template, then that template will be subsituted +-- using the same table. +-- @param tbl a table of name-value pairs. +function Template:indent_substitute(tbl) + assert_arg(1,tbl,'table') + if not self.strings then + self.strings = split(self.tmpl,'\n') + end + -- the idea is to substitute line by line, grabbing any spaces as + -- well as the $var. If the value to be substituted contains newlines, + -- then we split that into lines and adjust the indent before inserting. + local function subst(line) + return line:gsub('(%s*)%$([%w_]+)',function(sp,f) + local subtmpl + local s = tbl[f] + if not s then error("not present in table "..f) end + if getmetatable(s) == Template then + subtmpl = s + s = s.tmpl + else + s = tostring(s) + end + if s:find '\n' then + s = _indent(s,sp) + end + if subtmpl then return _substitute(s,tbl) + else return s + end + end) + end + local lines = imap(subst,self.strings) + return concat(lines,'\n')..'\n' +end + +------- Python-style formatting operator ------ +-- (see the lua-users wiki) -- + +function text.format_operator() + + local format = string.format + + -- a more forgiving version of string.format, which applies + -- tostring() to any value with a %s format. + local function formatx (fmt,...) + local args = {...} + local i = 1 + for p in fmt:gmatch('%%.') do + if p == '%s' and type(args[i]) ~= 'string' then + args[i] = tostring(args[i]) + end + i = i + 1 + end + return format(fmt,unpack(args)) + end + + local function basic_subst(s,t) + return (s:gsub('%$([%w_]+)',t)) + end + + -- Note this goes further than the original, and will allow these cases: + -- 1. a single value + -- 2. a list of values + -- 3. a map of var=value pairs + -- 4. a function, as in gsub + -- For the second two cases, it uses $-variable substituion. + getmetatable("").__mod = function(a, b) + if b == nil then + return a + elseif type(b) == "table" and getmetatable(b) == nil then + if #b == 0 then -- assume a map-like table + return _substitute(a,b,true) + else + return formatx(a,unpack(b)) + end + elseif type(b) == 'function' then + return basic_subst(a,b) + else + return formatx(a,b) + end + end +end + +return text diff --git a/framework/lualib/thirdparty/pl/types.lua b/framework/lualib/thirdparty/pl/types.lua new file mode 100755 index 0000000..7e2fd6b --- /dev/null +++ b/framework/lualib/thirdparty/pl/types.lua @@ -0,0 +1,183 @@ +---- Dealing with Detailed Type Information + +-- Dependencies `pl.utils` +-- @module pl.types + +local utils = require 'pl.utils' +local math_ceil = math.ceil +local assert_arg = utils.assert_arg +local types = {} + +--- is the object either a function or a callable object?. +-- @param obj Object to check. +function types.is_callable (obj) + return type(obj) == 'function' or getmetatable(obj) and getmetatable(obj).__call and true +end + +--- is the object of the specified type?. +-- If the type is a string, then use type, otherwise compare with metatable. +-- +-- NOTE: this function is imported from `utils.is_type`. +-- @param obj An object to check +-- @param tp The expected type +-- @function is_type +-- @see utils.is_type +types.is_type = utils.is_type + +local fileMT = getmetatable(io.stdout) + +--- a string representation of a type. +-- For tables and userdata with metatables, we assume that the metatable has a `_name` +-- field. If the field is not present it will return 'unknown table' or +-- 'unknown userdata'. +-- Lua file objects return the type 'file'. +-- @param obj an object +-- @return a string like 'number', 'table', 'file' or 'List' +function types.type (obj) + local t = type(obj) + if t == 'table' or t == 'userdata' then + local mt = getmetatable(obj) + if mt == fileMT then + return 'file' + elseif mt == nil then + return t + else + -- TODO: the "unknown" is weird, it should just return the type + return mt._name or "unknown "..t + end + else + return t + end +end + +--- is this number an integer? +-- @param x a number +-- @raise error if x is not a number +-- @return boolean +function types.is_integer (x) + return math_ceil(x)==x +end + +--- Check if the object is "empty". +-- An object is considered empty if it is: +-- +-- - `nil` +-- - a table with out any items (key-value pairs or indexes) +-- - a string with no content ("") +-- - not a nil/table/string +-- @param o The object to check if it is empty. +-- @param ignore_spaces If the object is a string and this is true the string is +-- considered empty if it only contains spaces. +-- @return `true` if the object is empty, otherwise a falsy value. +function types.is_empty(o, ignore_spaces) + if o == nil then + return true + elseif type(o) == "table" then + return next(o) == nil + elseif type(o) == "string" then + return o == "" or (not not ignore_spaces and (not not o:find("^%s+$"))) + else + return true + end +end + +local function check_meta (val) + if type(val) == 'table' then return true end + return getmetatable(val) +end + +--- is an object 'array-like'? +-- An object is array like if: +-- +-- - it is a table, or +-- - it has a metatable with `__len` and `__index` methods +-- +-- NOTE: since `__len` is 5.2+, on 5.1 is usually returns `false` for userdata +-- @param val any value. +-- @return `true` if the object is array-like, otherwise a falsy value. +function types.is_indexable (val) + local mt = check_meta(val) + if mt == true then return true end + return mt and mt.__len and mt.__index and true +end + +--- can an object be iterated over with `pairs`? +-- An object is iterable if: +-- +-- - it is a table, or +-- - it has a metatable with a `__pairs` meta method +-- +-- NOTE: since `__pairs` is 5.2+, on 5.1 is usually returns `false` for userdata +-- @param val any value. +-- @return `true` if the object is iterable, otherwise a falsy value. +function types.is_iterable (val) + local mt = check_meta(val) + if mt == true then return true end + return mt and mt.__pairs and true +end + +--- can an object accept new key/pair values? +-- An object is iterable if: +-- +-- - it is a table, or +-- - it has a metatable with a `__newindex` meta method +-- +-- @param val any value. +-- @return `true` if the object is writeable, otherwise a falsy value. +function types.is_writeable (val) + local mt = check_meta(val) + if mt == true then return true end + return mt and mt.__newindex and true +end + +-- Strings that should evaluate to true. -- TODO: add on/off ??? +local trues = { yes=true, y=true, ["true"]=true, t=true, ["1"]=true } +-- Conditions types should evaluate to true. +local true_types = { + boolean=function(o, true_strs, check_objs) return o end, + string=function(o, true_strs, check_objs) + o = o:lower() + if trues[o] then + return true + end + -- Check alternative user provided strings. + for _,v in ipairs(true_strs or {}) do + if type(v) == "string" and o == v:lower() then + return true + end + end + return false + end, + number=function(o, true_strs, check_objs) return o ~= 0 end, + table=function(o, true_strs, check_objs) if check_objs and next(o) ~= nil then return true end return false end +} +--- Convert to a boolean value. +-- True values are: +-- +-- * boolean: true. +-- * string: 'yes', 'y', 'true', 't', '1' or additional strings specified by `true_strs`. +-- * number: Any non-zero value. +-- * table: Is not empty and `check_objs` is true. +-- * everything else: Is not `nil` and `check_objs` is true. +-- +-- @param o The object to evaluate. +-- @param[opt] true_strs optional Additional strings that when matched should evaluate to true. Comparison is case insensitive. +-- This should be a List of strings. E.g. "ja" to support German. +-- @param[opt] check_objs True if objects should be evaluated. +-- @return true if the input evaluates to true, otherwise false. +function types.to_bool(o, true_strs, check_objs) + local true_func + if true_strs then + assert_arg(2, true_strs, "table") + end + true_func = true_types[type(o)] + if true_func then + return true_func(o, true_strs, check_objs) + elseif check_objs and o ~= nil then + return true + end + return false +end + + +return types diff --git a/framework/lualib/thirdparty/pl/url.lua b/framework/lualib/thirdparty/pl/url.lua new file mode 100755 index 0000000..8c7cfeb --- /dev/null +++ b/framework/lualib/thirdparty/pl/url.lua @@ -0,0 +1,51 @@ +--- Python-style URL quoting library. +-- +-- @module pl.url + +local url = {} + +local function quote_char(c) + return string.format("%%%02X", string.byte(c)) +end + +--- Quote the url, replacing special characters using the '%xx' escape. +-- @string s the string +-- @bool quote_plus Also escape slashes and replace spaces by plus signs. +-- @return The quoted string, or if `s` wasn't a string, just plain unaltered `s`. +function url.quote(s, quote_plus) + if type(s) ~= "string" then + return s + end + + s = s:gsub("\n", "\r\n") + s = s:gsub("([^A-Za-z0-9 %-_%./])", quote_char) + if quote_plus then + s = s:gsub(" ", "+") + s = s:gsub("/", quote_char) + else + s = s:gsub(" ", "%%20") + end + + return s +end + +local function unquote_char(h) + return string.char(tonumber(h, 16)) +end + +--- Unquote the url, replacing '%xx' escapes and plus signs. +-- @string s the string +-- @return The unquoted string, or if `s` wasn't a string, just plain unaltered `s`. +function url.unquote(s) + if type(s) ~= "string" then + return s + end + + s = s:gsub("+", " ") + s = s:gsub("%%(%x%x)", unquote_char) + s = s:gsub("\r\n", "\n") + + return s +end + +return url diff --git a/framework/lualib/thirdparty/pl/utils.lua b/framework/lualib/thirdparty/pl/utils.lua new file mode 100755 index 0000000..a8bf360 --- /dev/null +++ b/framework/lualib/thirdparty/pl/utils.lua @@ -0,0 +1,715 @@ +--- Generally useful routines. +-- See @{01-introduction.md.Generally_useful_functions|the Guide}. +-- +-- Dependencies: `pl.compat`, all exported fields and functions from +-- `pl.compat` are also available in this module. +-- +-- @module pl.utils +local format = string.format +local compat = require 'pl.compat' +local stdout = io.stdout +local append = table.insert +local _unpack = table.unpack -- always injected by 'compat' + +local is_windows = compat.is_windows +local err_mode = 'default' +local raise +local operators +local _function_factories = {} + + +local utils = { _VERSION = "1.9.2" } +for k, v in pairs(compat) do utils[k] = v end + +--- Some standard patterns +-- @table patterns +utils.patterns = { + FLOAT = '[%+%-%d]%d*%.?%d*[eE]?[%+%-]?%d*', -- floating point number + INTEGER = '[+%-%d]%d*', -- integer number + IDEN = '[%a_][%w_]*', -- identifier + FILE = '[%a%.\\][:%][%w%._%-\\]*', -- file +} + + +--- Standard meta-tables as used by other Penlight modules +-- @table stdmt +-- @field List the List metatable +-- @field Map the Map metatable +-- @field Set the Set metatable +-- @field MultiMap the MultiMap metatable +utils.stdmt = { + List = {_name='List'}, + Map = {_name='Map'}, + Set = {_name='Set'}, + MultiMap = {_name='MultiMap'}, +} + + +--- pack an argument list into a table. +-- @param ... any arguments +-- @return a table with field `n` set to the length +-- @function utils.pack +-- @see compat.pack +utils.pack = table.pack -- added here to be symmetrical with unpack + +--- unpack a table and return its contents. +-- +-- NOTE: this implementation differs from the Lua implementation in the way +-- that this one DOES honor the `n` field in the table `t`, such that it is 'nil-safe'. +-- @param t table to unpack +-- @param[opt] i index from which to start unpacking, defaults to 1 +-- @param[opt] t index of the last element to unpack, defaults to `t.n` or `#t` +-- @return multiple return values from the table +-- @function utils.unpack +-- @see compat.unpack +-- @usage +-- local t = table.pack(nil, nil, nil, 4) +-- local a, b, c, d = table.unpack(t) -- this `unpack` is NOT nil-safe, so d == nil +-- +-- local a, b, c, d = utils.unpack(t) -- this is nil-safe, so d == 4 +function utils.unpack(t, i, j) + return _unpack(t, i or 1, j or t.n or #t) +end + +--- print an arbitrary number of arguments using a format. +-- Output will be sent to `stdout`. +-- @param fmt The format (see `string.format`) +-- @param ... Extra arguments for format +function utils.printf(fmt, ...) + utils.assert_string(1, fmt) + utils.fprintf(stdout, fmt, ...) +end + +--- write an arbitrary number of arguments to a file using a format. +-- @param f File handle to write to. +-- @param fmt The format (see `string.format`). +-- @param ... Extra arguments for format +function utils.fprintf(f,fmt,...) + utils.assert_string(2,fmt) + f:write(format(fmt,...)) +end + +do + local function import_symbol(T,k,v,libname) + local key = rawget(T,k) + -- warn about collisions! + if key and k ~= '_M' and k ~= '_NAME' and k ~= '_PACKAGE' and k ~= '_VERSION' then + utils.fprintf(io.stderr,"warning: '%s.%s' will not override existing symbol\n",libname,k) + return + end + rawset(T,k,v) + end + + local function lookup_lib(T,t) + for k,v in pairs(T) do + if v == t then return k end + end + return '?' + end + + local already_imported = {} + + --- take a table and 'inject' it into the local namespace. + -- @param t The table (table), or module name (string), defaults to this `utils` module table + -- @param T An optional destination table (defaults to callers environment) + function utils.import(t,T) + T = T or _G + t = t or utils + if type(t) == 'string' then + t = require (t) + end + local libname = lookup_lib(T,t) + if already_imported[t] then return end + already_imported[t] = libname + for k,v in pairs(t) do + import_symbol(T,k,v,libname) + end + end +end + +--- return either of two values, depending on a condition. +-- @param cond A condition +-- @param value1 Value returned if cond is truthy +-- @param value2 Value returned if cond is falsy +function utils.choose(cond, value1, value2) + return cond and value1 or value2 +end + +--- convert an array of values to strings. +-- @param t a list-like table +-- @param[opt] temp (table) buffer to use, otherwise allocate +-- @param[opt] tostr custom tostring function, called with (value,index). Defaults to `tostring`. +-- @return the converted buffer +function utils.array_tostring (t,temp,tostr) + temp, tostr = temp or {}, tostr or tostring + for i = 1,#t do + temp[i] = tostr(t[i],i) + end + return temp +end + + + +--- is the object of the specified type? +-- If the type is a string, then use type, otherwise compare with metatable +-- @param obj An object to check +-- @param tp String of what type it should be +-- @return boolean +-- @usage utils.is_type("hello world", "string") --> true +-- -- or check metatable +-- local my_mt = {} +-- local my_obj = setmetatable(my_obj, my_mt) +-- utils.is_type(my_obj, my_mt) --> true +function utils.is_type (obj,tp) + if type(tp) == 'string' then return type(obj) == tp end + local mt = getmetatable(obj) + return tp == mt +end + +--- Error handling +-- @section Error-handling + +--- assert that the given argument is in fact of the correct type. +-- @param n argument index +-- @param val the value +-- @param tp the type +-- @param verify an optional verification function +-- @param msg an optional custom message +-- @param lev optional stack position for trace, default 2 +-- @return the validated value +-- @raise if `val` is not the correct type +-- @usage +-- local param1 = assert_arg(1,"hello",'table') --> error: argument 1 expected a 'table', got a 'string' +-- local param4 = assert_arg(4,'!@#$%^&*','string',path.isdir,'not a directory') +-- --> error: argument 4: '!@#$%^&*' not a directory +function utils.assert_arg (n,val,tp,verify,msg,lev) + if type(val) ~= tp then + error(("argument %d expected a '%s', got a '%s'"):format(n,tp,type(val)),lev or 2) + end + if verify and not verify(val) then + error(("argument %d: '%s' %s"):format(n,val,msg),lev or 2) + end + return val +end + +--- process a function argument. +-- This is used throughout Penlight and defines what is meant by a function: +-- Something that is callable, or an operator string as defined by pl.operator, +-- such as '>' or '#'. If a function factory has been registered for the type, it will +-- be called to get the function. +-- @param idx argument index +-- @param f a function, operator string, or callable object +-- @param msg optional error message +-- @return a callable +-- @raise if idx is not a number or if f is not callable +function utils.function_arg (idx,f,msg) + utils.assert_arg(1,idx,'number') + local tp = type(f) + if tp == 'function' then return f end -- no worries! + -- ok, a string can correspond to an operator (like '==') + if tp == 'string' then + if not operators then operators = require 'pl.operator'.optable end + local fn = operators[f] + if fn then return fn end + local fn, err = utils.string_lambda(f) + if not fn then error(err..': '..f) end + return fn + elseif tp == 'table' or tp == 'userdata' then + local mt = getmetatable(f) + if not mt then error('not a callable object',2) end + local ff = _function_factories[mt] + if not ff then + if not mt.__call then error('not a callable object',2) end + return f + else + return ff(f) -- we have a function factory for this type! + end + end + if not msg then msg = " must be callable" end + if idx > 0 then + error("argument "..idx..": "..msg,2) + else + error(msg,2) + end +end + + +--- assert the common case that the argument is a string. +-- @param n argument index +-- @param val a value that must be a string +-- @return the validated value +-- @raise val must be a string +-- @usage +-- local val = 42 +-- local param2 = utils.assert_string(2, val) --> error: argument 2 expected a 'string', got a 'number' +function utils.assert_string (n, val) + return utils.assert_arg(n,val,'string',nil,nil,3) +end + +--- control the error strategy used by Penlight. +-- This is a global setting that controls how `utils.raise` behaves: +-- +-- - 'default': return `nil + error` (this is the default) +-- - 'error': throw a Lua error +-- - 'quit': exit the program +-- +-- @param mode either 'default', 'quit' or 'error' +-- @see utils.raise +function utils.on_error (mode) + mode = tostring(mode) + if ({['default'] = 1, ['quit'] = 2, ['error'] = 3})[mode] then + err_mode = mode + else + -- fail loudly + local err = "Bad argument expected string; 'default', 'quit', or 'error'. Got '"..tostring(mode).."'" + if err_mode == 'default' then + error(err, 2) -- even in 'default' mode fail loud in this case + end + raise(err) + end +end + +--- used by Penlight functions to return errors. Its global behaviour is controlled +-- by `utils.on_error`. +-- To use this function you MUST use it in conjunction with `return`, since it might +-- return `nil + error`. +-- @param err the error string. +-- @see utils.on_error +-- @usage +-- if some_condition then +-- return utils.raise("some condition was not met") -- MUST use 'return'! +-- end +function utils.raise (err) + if err_mode == 'default' then + return nil, err + elseif err_mode == 'quit' then + return utils.quit(err) + else + error(err, 2) + end +end +raise = utils.raise + + + +--- File handling +-- @section files + +--- return the contents of a file as a string +-- @param filename The file path +-- @param is_bin open in binary mode +-- @return file contents +function utils.readfile(filename,is_bin) + local mode = is_bin and 'b' or '' + utils.assert_string(1,filename) + local f,open_err = io.open(filename,'r'..mode) + if not f then return raise (open_err) end + local res,read_err = f:read('*a') + f:close() + if not res then + -- Errors in io.open have "filename: " prefix, + -- error in file:read don't, add it. + return raise (filename..": "..read_err) + end + return res +end + +--- write a string to a file +-- @param filename The file path +-- @param str The string +-- @param is_bin open in binary mode +-- @return true or nil +-- @return error message +-- @raise error if filename or str aren't strings +function utils.writefile(filename,str,is_bin) + local mode = is_bin and 'b' or '' + utils.assert_string(1,filename) + utils.assert_string(2,str) + local f,err = io.open(filename,'w'..mode) + if not f then return raise(err) end + local ok, write_err = f:write(str) + f:close() + if not ok then + -- Errors in io.open have "filename: " prefix, + -- error in file:write don't, add it. + return raise (filename..": "..write_err) + end + return true +end + +--- return the contents of a file as a list of lines +-- @param filename The file path +-- @return file contents as a table +-- @raise error if filename is not a string +function utils.readlines(filename) + utils.assert_string(1,filename) + local f,err = io.open(filename,'r') + if not f then return raise(err) end + local res = {} + for line in f:lines() do + append(res,line) + end + f:close() + return res +end + +--- OS functions +-- @section OS-functions + +--- execute a shell command and return the output. +-- This function redirects the output to tempfiles and returns the content of those files. +-- @param cmd a shell command +-- @param bin boolean, if true, read output as binary file +-- @return true if successful +-- @return actual return code +-- @return stdout output (string) +-- @return errout output (string) +function utils.executeex(cmd, bin) + local outfile = os.tmpname() + local errfile = os.tmpname() + + if is_windows and not outfile:find(':') then + outfile = os.getenv('TEMP')..outfile + errfile = os.getenv('TEMP')..errfile + end + cmd = cmd .. " > " .. utils.quote_arg(outfile) .. " 2> " .. utils.quote_arg(errfile) + + local success, retcode = utils.execute(cmd) + local outcontent = utils.readfile(outfile, bin) + local errcontent = utils.readfile(errfile, bin) + os.remove(outfile) + os.remove(errfile) + return success, retcode, (outcontent or ""), (errcontent or "") +end + +--- Quote and escape an argument of a command. +-- Quotes a single (or list of) argument(s) of a command to be passed +-- to `os.execute`, `pl.utils.execute` or `pl.utils.executeex`. +-- @param argument (string or table/list) the argument to quote. If a list then +-- all arguments in the list will be returned as a single string quoted. +-- @return quoted and escaped argument. +-- @usage +-- local options = utils.quote_arg { +-- "-lluacov", +-- "-e", +-- "utils = print(require('pl.utils')._VERSION", +-- } +-- -- returns: -lluacov -e 'utils = print(require('\''pl.utils'\'')._VERSION' +function utils.quote_arg(argument) + if type(argument) == "table" then + -- encode an entire table + local r = {} + for i, arg in ipairs(argument) do + r[i] = utils.quote_arg(arg) + end + + return table.concat(r, " ") + end + -- only a single argument + if is_windows then + if argument == "" or argument:find('[ \f\t\v]') then + -- Need to quote the argument. + -- Quotes need to be escaped with backslashes; + -- additionally, backslashes before a quote, escaped or not, + -- need to be doubled. + -- See documentation for CommandLineToArgvW Windows function. + argument = '"' .. argument:gsub([[(\*)"]], [[%1%1\"]]):gsub([[\+$]], "%0%0") .. '"' + end + + -- os.execute() uses system() C function, which on Windows passes command + -- to cmd.exe. Escape its special characters. + return (argument:gsub('["^<>!|&%%]', "^%0")) + else + if argument == "" or argument:find('[^a-zA-Z0-9_@%+=:,./-]') then + -- To quote arguments on posix-like systems use single quotes. + -- To represent an embedded single quote close quoted string ('), + -- add escaped quote (\'), open quoted string again ('). + argument = "'" .. argument:gsub("'", [['\'']]) .. "'" + end + + return argument + end +end + +--- error out of this program gracefully. +-- @param[opt] code The exit code, defaults to -`1` if omitted +-- @param msg The exit message will be sent to `stderr` (will be formatted with the extra parameters) +-- @param ... extra arguments for message's format' +-- @see utils.fprintf +-- @usage utils.quit(-1, "Error '%s' happened", "42") +-- -- is equivalent to +-- utils.quit("Error '%s' happened", "42") --> Error '42' happened +function utils.quit(code, msg, ...) + if type(code) == 'string' then + utils.fprintf(io.stderr, code, msg, ...) + io.stderr:write('\n') + code = -1 -- TODO: this is odd, see the test. Which returns 255 as exit code + elseif msg then + utils.fprintf(io.stderr, msg, ...) + io.stderr:write('\n') + end + os.exit(code, true) +end + + +--- String functions +-- @section string-functions + +--- escape any Lua 'magic' characters in a string +-- @param s The input string +function utils.escape(s) + utils.assert_string(1,s) + return (s:gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1')) +end + +--- split a string into a list of strings separated by a delimiter. +-- @param s The input string +-- @param re optional A Lua string pattern; defaults to '%s+' +-- @param plain optional If truthy don't use Lua patterns +-- @param n optional maximum number of elements (if there are more, the last will remian un-split) +-- @return a list-like table +-- @raise error if s is not a string +-- @see splitv +function utils.split(s,re,plain,n) + utils.assert_string(1,s) + local find,sub,append = string.find, string.sub, table.insert + local i1,ls = 1,{} + if not re then re = '%s+' end + if re == '' then return {s} end + while true do + local i2,i3 = find(s,re,i1,plain) + if not i2 then + local last = sub(s,i1) + if last ~= '' then append(ls,last) end + if #ls == 1 and ls[1] == '' then + return {} + else + return ls + end + end + append(ls,sub(s,i1,i2-1)) + if n and #ls == n then + ls[#ls] = sub(s,i1) + return ls + end + i1 = i3+1 + end +end + +--- split a string into a number of return values. +-- Identical to `split` but returns multiple sub-strings instead of +-- a single list of sub-strings. +-- @param s the string +-- @param re A Lua string pattern; defaults to '%s+' +-- @param plain don't use Lua patterns +-- @param n optional maximum number of splits +-- @return n values +-- @usage first,next = splitv('user=jane=doe','=', false, 2) +-- assert(first == "user") +-- assert(next == "jane=doe") +-- @see split +function utils.splitv (s,re, plain, n) + return _unpack(utils.split(s,re, plain, n)) +end + + +--- Functional +-- @section functional + + +--- 'memoize' a function (cache returned value for next call). +-- This is useful if you have a function which is relatively expensive, +-- but you don't know in advance what values will be required, so +-- building a table upfront is wasteful/impossible. +-- @param func a function of at least one argument +-- @return a function with at least one argument, which is used as the key. +function utils.memoize(func) + local cache = {} + return function(k) + local res = cache[k] + if res == nil then + res = func(k) + cache[k] = res + end + return res + end +end + + +--- associate a function factory with a type. +-- A function factory takes an object of the given type and +-- returns a function for evaluating it +-- @tab mt metatable +-- @func fun a callable that returns a function +function utils.add_function_factory (mt,fun) + _function_factories[mt] = fun +end + +local function _string_lambda(f) + if f:find '^|' or f:find '_' then + local args,body = f:match '|([^|]*)|(.+)' + if f:find '_' then + args = '_' + body = f + else + if not args then return raise 'bad string lambda' end + end + local fstr = 'return function('..args..') return '..body..' end' + local fn,err = utils.load(fstr) + if not fn then return raise(err) end + fn = fn() + return fn + else + return raise 'not a string lambda' + end +end + + +--- an anonymous function as a string. This string is either of the form +-- '|args| expression' or is a function of one argument, '_' +-- @param lf function as a string +-- @return a function +-- @function utils.string_lambda +-- @usage +-- string_lambda '|x|x+1' (2) == 3 +-- string_lambda '_+1' (2) == 3 +utils.string_lambda = utils.memoize(_string_lambda) + + +--- bind the first argument of the function to a value. +-- @param fn a function of at least two values (may be an operator string) +-- @param p a value +-- @return a function such that f(x) is fn(p,x) +-- @raise same as @{function_arg} +-- @see func.bind1 +-- @usage local function f(msg, name) +-- print(msg .. " " .. name) +-- end +-- +-- local hello = utils.bind1(f, "Hello") +-- +-- print(hello("world")) --> "Hello world" +-- print(hello("sunshine")) --> "Hello sunshine" +function utils.bind1 (fn,p) + fn = utils.function_arg(1,fn) + return function(...) return fn(p,...) end +end + + +--- bind the second argument of the function to a value. +-- @param fn a function of at least two values (may be an operator string) +-- @param p a value +-- @return a function such that f(x) is fn(x,p) +-- @raise same as @{function_arg} +-- @usage local function f(a, b, c) +-- print(a .. " " .. b .. " " .. c) +-- end +-- +-- local hello = utils.bind1(f, "world") +-- +-- print(hello("Hello", "!")) --> "Hello world !" +-- print(hello("Bye", "?")) --> "Bye world ?" +function utils.bind2 (fn,p) + fn = utils.function_arg(1,fn) + return function(x,...) return fn(x,p,...) end +end + + + + +--- Deprecation +-- @section deprecation + +do + -- the default implementation + local deprecation_func = function(msg, trace) + warn(msg, "\n", trace) -- luacheck: ignore + end + + --- Sets a deprecation warning function. + -- An application can override this function to support proper output of + -- deprecation warnings. The warnings can be generated from libraries or + -- functions by calling `utils.raise_deprecation`. The default function + -- will write to the 'warn' system (introduced in Lua 5.4, or the compatibility + -- function from the `compat` module for earlier versions). + -- + -- Note: only applications should set/change this function, libraries should not. + -- @param func a callback with signature: `function(msg, trace)` both arguments are strings. + -- @see utils.raise_deprecation + -- @usage + -- -- write to the Nginx logs with OpenResty + -- utils.set_deprecation_func(function(msg, trace) + -- ngx.log(ngx.WARN, msg, " ", trace) + -- end) + -- + -- -- disable deprecation warnings + -- utils.set_deprecation_func() + function utils.set_deprecation_func(func) + if func == nil then + deprecation_func = function() end + else + utils.assert_arg(1, func, "function") + deprecation_func = func + end + end + + --- raises a deprecation warning. + -- For options see the usage example below. + -- + -- Note: the `opts.deprecated_after` field is the last version in which + -- a feature or option was NOT YET deprecated! Because when writing the code it + -- is quite often not known in what version the code will land. But the last + -- released version is usually known. + -- @param opts options table + -- @see utils.set_deprecation_func + -- @usage + -- warn("@on") -- enable Lua warnings, they are usually off by default + -- + -- function stringx.islower(str) + -- raise_deprecation { + -- source = "Penlight " .. utils._VERSION, -- optional + -- message = "function 'islower' was renamed to 'is_lower'" -- required + -- version_removed = "2.0.0", -- optional + -- deprecated_after = "1.2.3", -- optional + -- } + -- return stringx.is_lower(str) + -- end + -- -- output: "[Penlight 1.9.2] function 'islower' was renamed to 'is_lower' (deprecated after 1.2.3, scheduled for removal in 2.0.0)" + function utils.raise_deprecation(opts) + utils.assert_arg(1, opts, "table") + if type(opts.message) ~= "string" then + error("field 'message' of the options table must be a string", 2) + end + local trace = debug.traceback("", 2):match("[\n%s]*(.-)$") + local msg + if opts.deprecated_after and opts.version_removed then + msg = (" (deprecated after %s, scheduled for removal in %s)"):format( + tostring(opts.deprecated_after), tostring(opts.version_removed)) + elseif opts.deprecated_after then + msg = (" (deprecated after %s)"):format(tostring(opts.deprecated_after)) + elseif opts.version_removed then + msg = (" (scheduled for removal in %s)"):format(tostring(opts.version_removed)) + else + msg = "" + end + + msg = opts.message .. msg + + if opts.source then + msg = "[" .. opts.source .."] " .. msg + else + if msg:sub(1,1) == "@" then + -- in Lua 5.4 "@" prefixed messages are control messages to the warn system + error("message cannot start with '@'", 2) + end + end + + deprecation_func(msg, trace) + end + +end + + +return utils + + diff --git a/framework/lualib/thirdparty/pl/xml.lua b/framework/lualib/thirdparty/pl/xml.lua new file mode 100755 index 0000000..4650a32 --- /dev/null +++ b/framework/lualib/thirdparty/pl/xml.lua @@ -0,0 +1,776 @@ +--- XML LOM Utilities. +-- +-- This implements some useful things on [LOM](http://matthewwild.co.uk/projects/luaexpat/lom.html) documents, such as returned by `lxp.lom.parse`. +-- In particular, it can convert LOM back into XML text, with optional pretty-printing control. +-- It is s based on stanza.lua from [Prosody](http://hg.prosody.im/trunk/file/4621c92d2368/util/stanza.lua) +-- +-- > d = xml.parse "alice" +-- > = d +-- alice +-- > = xml.tostring(d,'',' ') +-- +-- alice +-- +-- +-- Can be used as a lightweight one-stop-shop for simple XML processing; a simple XML parser is included +-- but the default is to use `lxp.lom` if it can be found. +--
+-- Prosody IM
+-- Copyright (C) 2008-2010 Matthew Wild
+-- Copyright (C) 2008-2010 Waqas Hussain--
+-- classic Lua XML parser by Roberto Ierusalimschy.
+-- modified to output LOM format.
+-- http://lua-users.org/wiki/LuaXml
+-- 
+-- See @{06-data.md.XML|the Guide} +-- +-- Dependencies: `pl.utils` +-- +-- Soft Dependencies: `lxp.lom` (fallback is to use basic Lua parser) +-- @module pl.xml + +local utils = require 'pl.utils' +local split = utils.split; +local t_insert = table.insert; +local t_concat = table.concat; +local t_remove = table.remove; +local s_match = string.match; +local tostring = tostring; +local setmetatable = setmetatable; +local getmetatable = getmetatable; +local pairs = pairs; +local ipairs = ipairs; +local type = type; +local next = next; +local print = print; +local unpack = utils.unpack; +local s_gsub = string.gsub; +local s_find = string.find; +local pcall,require,io = pcall,require,io + +local _M = {} +local Doc = { __type = "doc" }; +Doc.__index = Doc; + +--- create a new document node. +-- @param tag the tag name +-- @param attr optional attributes (table of name-value pairs) +function _M.new(tag, attr) + local doc = { tag = tag, attr = attr or {}, last_add = {}}; + return setmetatable(doc, Doc); +end + +--- parse an XML document. By default, this uses lxp.lom.parse, but +-- falls back to basic_parse, or if use_basic is true +-- @param text_or_file file or string representation +-- @param is_file whether text_or_file is a file name or not +-- @param use_basic do a basic parse +-- @return a parsed LOM document with the document metatatables set +-- @return nil, error the error can either be a file error or a parse error +function _M.parse(text_or_file, is_file, use_basic) + local parser,status,lom + if use_basic then parser = _M.basic_parse + else + status,lom = pcall(require,'lxp.lom') + if not status then parser = _M.basic_parse else parser = lom.parse end + end + if is_file then + local f,err = io.open(text_or_file) + if not f then return nil,err end + text_or_file = f:read '*a' + f:close() + end + local doc,err = parser(text_or_file) + if not doc then return nil,err end + if lom then + _M.walk(doc,false,function(_,d) + setmetatable(d,Doc) + end) + end + return doc +end + +---- convenient function to add a document node, This updates the last inserted position. +-- @param tag a tag name +-- @param attrs optional set of attributes (name-string pairs) +function Doc:addtag(tag, attrs) + local s = _M.new(tag, attrs); + (self.last_add[#self.last_add] or self):add_direct_child(s); + t_insert(self.last_add, s); + return self; +end + +--- convenient function to add a text node. This updates the last inserted position. +-- @param text a string +function Doc:text(text) + (self.last_add[#self.last_add] or self):add_direct_child(text); + return self; +end + +---- go up one level in a document +function Doc:up() + t_remove(self.last_add); + return self; +end + +function Doc:reset() + local last_add = self.last_add; + for i = 1,#last_add do + last_add[i] = nil; + end + return self; +end + +--- append a child to a document directly. +-- @param child a child node (either text or a document) +function Doc:add_direct_child(child) + t_insert(self, child); +end + +--- append a child to a document at the last element added +-- @param child a child node (either text or a document) +function Doc:add_child(child) + (self.last_add[#self.last_add] or self):add_direct_child(child); + return self; +end + +--accessing attributes: useful not to have to expose implementation (attr) +--but also can allow attr to be nil in any future optimizations + +--- set attributes of a document node. +-- @param t a table containing attribute/value pairs +function Doc:set_attribs (t) + for k,v in pairs(t) do + self.attr[k] = v + end +end + +--- set a single attribute of a document node. +-- @param a attribute +-- @param v its value +function Doc:set_attrib(a,v) + self.attr[a] = v +end + +--- access the attributes of a document node. +function Doc:get_attribs() + return self.attr +end + +local function is_text(s) return type(s) == 'string' end + +--- function to create an element with a given tag name and a set of children. +-- @param tag a tag name +-- @param items either text or a table where the hash part is the attributes and the list part is the children. +function _M.elem(tag,items) + local s = _M.new(tag) + if is_text(items) then items = {items} end + if _M.is_tag(items) then + t_insert(s,items) + elseif type(items) == 'table' then + for k,v in pairs(items) do + if is_text(k) then + s.attr[k] = v + t_insert(s.attr,k) + else + s[k] = v + end + end + end + return s +end + +--- given a list of names, return a number of element constructors. +-- @param list a list of names, or a comma-separated string. +-- @usage local parent,children = doc.tags 'parent,children'
+-- doc = parent {child 'one', child 'two'} +function _M.tags(list) + local ctors = {} + if is_text(list) then list = split(list,'%s*,%s*') end + for _,tag in ipairs(list) do + local ctor = function(items) return _M.elem(tag,items) end + t_insert(ctors,ctor) + end + return unpack(ctors) +end + +local templ_cache = {} + +local function template_cache (templ) + if is_text(templ) then + if templ_cache[templ] then + templ = templ_cache[templ] + else + local str,err = templ + templ,err = _M.parse(str,false,true) + if not templ then return nil,err end + templ_cache[str] = templ + end + elseif not _M.is_tag(templ) then + return nil, "template is not a document" + end + return templ +end + +local function is_data(data) + return #data == 0 or type(data[1]) ~= 'table' +end + +local function prepare_data(data) + -- a hack for ensuring that $1 maps to first element of data, etc. + -- Either this or could change the gsub call just below. + for i,v in ipairs(data) do + data[tostring(i)] = v + end +end + +--- create a substituted copy of a document, +-- @param templ may be a document or a string representation which will be parsed and cached +-- @param data a table of name-value pairs or a list of such tables +-- @return an XML document +function Doc.subst(templ, data) + local err + if type(data) ~= 'table' or not next(data) then return nil, "data must be a non-empty table" end + if is_data(data) then + prepare_data(data) + end + templ,err = template_cache(templ) + if err then return nil, err end + local function _subst(item) + return _M.clone(templ,function(s) + return s:gsub('%$(%w+)',item) + end) + end + if is_data(data) then return _subst(data) end + local list = {} + for _,item in ipairs(data) do + prepare_data(item) + t_insert(list,_subst(item)) + end + if data.tag then + list = _M.elem(data.tag,list) + end + return list +end + + +--- get the first child with a given tag name. +-- @param tag the tag name +function Doc:child_with_name(tag) + for _, child in ipairs(self) do + if child.tag == tag then return child; end + end +end + +local _children_with_name +function _children_with_name(self,tag,list,recurse) + for _, child in ipairs(self) do if type(child) == 'table' then + if child.tag == tag then t_insert(list,child) end + if recurse then _children_with_name(child,tag,list,recurse) end + end end +end + +--- get all elements in a document that have a given tag. +-- @param tag a tag name +-- @param dont_recurse optionally only return the immediate children with this tag name +-- @return a list of elements +function Doc:get_elements_with_name(tag,dont_recurse) + local res = {} + _children_with_name(self,tag,res,not dont_recurse) + return res +end + +-- iterate over all children of a document node, including text nodes. +function Doc:children() + local i = 0; + return function (a) + i = i + 1 + return a[i]; + end, self, i; +end + +-- return the first child element of a node, if it exists. +function Doc:first_childtag() + if #self == 0 then return end + for _,t in ipairs(self) do + if type(t) == 'table' then return t end + end +end + +function Doc:matching_tags(tag, xmlns) + xmlns = xmlns or self.attr.xmlns; + local tags = self; + local start_i, max_i, v = 1, #tags; + return function () + for i=start_i,max_i do + v = tags[i]; + if (not tag or v.tag == tag) + and (not xmlns or xmlns == v.attr.xmlns) then + start_i = i+1; + return v; + end + end + end, tags, start_i; +end + +--- iterate over all child elements of a document node. +function Doc:childtags() + local i = 0; + return function (a) + local v + repeat + i = i + 1 + v = self[i] + if v and type(v) == 'table' then return v; end + until not v + end, self[1], i; +end + +--- visit child element of a node and call a function, possibility modifying the document. +-- @param callback a function passed the node (text or element). If it returns nil, that node will be removed. +-- If it returns a value, that will replace the current node. +function Doc:maptags(callback) + local is_tag = _M.is_tag + local i = 1; + while i <= #self do + if is_tag(self[i]) then + local ret = callback(self[i]); + if ret == nil then + t_remove(self, i); + else + self[i] = ret; + i = i + 1; + end + end + end + return self; +end + +local xml_escape +do + local escape_table = { ["'"] = "'", ["\""] = """, ["<"] = "<", [">"] = ">", ["&"] = "&" }; + function xml_escape(str) return (s_gsub(str, "['&<>\"]", escape_table)); end + _M.xml_escape = xml_escape; +end + +-- pretty printing +-- if indent, then put each new tag on its own line +-- if attr_indent, put each new attribute on its own line +local function _dostring(t, buf, self, xml_escape, parentns, idn, indent, attr_indent) + local nsid = 0; + local tag = t.tag + local lf,alf = ""," " + if indent then lf = '\n'..idn end + if attr_indent then alf = '\n'..idn..attr_indent end + t_insert(buf, lf.."<"..tag); + local function write_attr(k,v) + if s_find(k, "\1", 1, true) then + local ns, attrk = s_match(k, "^([^\1]*)\1?(.*)$"); + nsid = nsid + 1; + t_insert(buf, " xmlns:ns"..nsid.."='"..xml_escape(ns).."' ".."ns"..nsid..":"..attrk.."='"..xml_escape(v).."'"); + elseif not(k == "xmlns" and v == parentns) then + t_insert(buf, alf..k.."='"..xml_escape(v).."'"); + end + end + -- it's useful for testing to have predictable attribute ordering, if available + if #t.attr > 0 then + for _,k in ipairs(t.attr) do + write_attr(k,t.attr[k]) + end + else + for k, v in pairs(t.attr) do + write_attr(k,v) + end + end + local len,has_children = #t; + if len == 0 then + local out = "/>" + if attr_indent then out = '\n'..idn..out end + t_insert(buf, out); + else + t_insert(buf, ">"); + for n=1,len do + local child = t[n]; + if child.tag then + self(child, buf, self, xml_escape, t.attr.xmlns,idn and idn..indent, indent, attr_indent ); + has_children = true + else -- text element + t_insert(buf, xml_escape(child)); + end + end + t_insert(buf, (has_children and lf or '')..""); + end +end + +---- pretty-print an XML document +--- @param t an XML document +--- @param idn an initial indent (indents are all strings) +--- @param indent an indent for each level +--- @param attr_indent if given, indent each attribute pair and put on a separate line +--- @param xml force prefacing with default or custom +--- @return a string representation +function _M.tostring(t,idn,indent, attr_indent, xml) + local buf = {}; + if xml then + if type(xml) == "string" then + buf[1] = xml + else + buf[1] = "" + end + end + _dostring(t, buf, _dostring, xml_escape, nil,idn,indent, attr_indent); + return t_concat(buf); +end + +Doc.__tostring = _M.tostring + +--- get the full text value of an element +function Doc:get_text() + local res = {} + for i,el in ipairs(self) do + if is_text(el) then t_insert(res,el) end + end + return t_concat(res); +end + +--- make a copy of a document +-- @param doc the original document +-- @param strsubst an optional function for handling string copying which could do substitution, etc. +function _M.clone(doc, strsubst) + local lookup_table = {}; + local function _copy(object,kind,parent) + if type(object) ~= "table" then + if strsubst and is_text(object) then return strsubst(object,kind,parent) + else return object + end + elseif lookup_table[object] then + return lookup_table[object] + end + local new_table = {}; + lookup_table[object] = new_table + local tag = object.tag + new_table.tag = _copy(tag,'*TAG',parent) + if object.attr then + local res = {} + for attr,value in pairs(object.attr) do + res[attr] = _copy(value,attr,object) + end + new_table.attr = res + end + for index = 1,#object do + local v = _copy(object[index],'*TEXT',object) + t_insert(new_table,v) + end + return setmetatable(new_table, getmetatable(object)) + end + + return _copy(doc) +end + +Doc.filter = _M.clone -- also available as method + +--- compare two documents. +-- @param t1 any value +-- @param t2 any value +function _M.compare(t1,t2) + local ty1 = type(t1) + local ty2 = type(t2) + if ty1 ~= ty2 then return false, 'type mismatch' end + if ty1 == 'string' then + return t1 == t2 and true or 'text '..t1..' ~= text '..t2 + end + if ty1 ~= 'table' or ty2 ~= 'table' then return false, 'not a document' end + if t1.tag ~= t2.tag then return false, 'tag '..t1.tag..' ~= tag '..t2.tag end + if #t1 ~= #t2 then return false, 'size '..#t1..' ~= size '..#t2..' for tag '..t1.tag end + -- compare attributes + for k,v in pairs(t1.attr) do + if t2.attr[k] ~= v then return false, 'mismatch attrib' end + end + for k,v in pairs(t2.attr) do + if t1.attr[k] ~= v then return false, 'mismatch attrib' end + end + -- compare children + for i = 1,#t1 do + local yes,err = _M.compare(t1[i],t2[i]) + if not yes then return err end + end + return true +end + +--- is this value a document element? +-- @param d any value +function _M.is_tag(d) + return type(d) == 'table' and is_text(d.tag) +end + +--- call the desired function recursively over the document. +-- @param doc the document +-- @param depth_first visit child notes first, then the current node +-- @param operation a function which will receive the current tag name and current node. +function _M.walk (doc, depth_first, operation) + if not depth_first then operation(doc.tag,doc) end + for _,d in ipairs(doc) do + if _M.is_tag(d) then + _M.walk(d,depth_first,operation) + end + end + if depth_first then operation(doc.tag,doc) end +end + +local html_empty_elements = { --lists all HTML empty (void) elements + br = true, + img = true, + meta = true, + frame = true, + area = true, + hr = true, + base = true, + col = true, + link = true, + input = true, + option = true, + param = true, + isindex = true, + embed = true, +} + +local escapes = { quot = "\"", apos = "'", lt = "<", gt = ">", amp = "&" } +local function unescape(str) return (str:gsub( "&(%a+);", escapes)); end + +--- Parse a well-formed HTML file as a string. +-- Tags are case-insenstive, DOCTYPE is ignored, and empty elements can be .. empty. +-- @param s the HTML +function _M.parsehtml (s) + return _M.basic_parse(s,false,true) +end + +--- Parse a simple XML document using a pure Lua parser based on Robero Ierusalimschy's original version. +-- @param s the XML document to be parsed. +-- @param all_text if true, preserves all whitespace. Otherwise only text containing non-whitespace is included. +-- @param html if true, uses relaxed HTML rules for parsing +function _M.basic_parse(s,all_text,html) + local t_insert,t_remove = table.insert,table.remove + local s_find,s_sub = string.find,string.sub + local stack = {} + local top = {} + + local function parseargs(s) + local arg = {} + s:gsub("([%w:%-_]+)%s*=%s*([\"'])(.-)%2", function (w, _, a) + if html then w = w:lower() end + arg[w] = unescape(a) + end) + if html then + s:gsub("([%w:%-_]+)%s*=%s*([^\"']+)%s*", function (w, a) + w = w:lower() + arg[w] = unescape(a) + end) + end + return arg + end + + t_insert(stack, top) + local ni,c,label,xarg, empty, _, istart + local i = 1 + local j + -- we're not interested in + _,istart = s_find(s,'^%s*<%?[^%?]+%?>%s*') + if not istart then -- or + _,istart = s_find(s,'^%s*%s*') + end + if istart then i = istart+1 end + while true do + ni,j,c,label,xarg, empty = s_find(s, "<([%/!]?)([%w:%-_]+)(.-)(%/?)>", i) + if not ni then break end + if c == "!" then -- comment + -- case where there's no space inside comment + if not (label:match '%-%-$' and xarg == '') then + if xarg:match '%-%-$' then -- we've grabbed it all + j = j - 2 + end + -- match end of comment + _,j = s_find(s, "-->", j, true) + end + else + local text = s_sub(s, i, ni-1) + if html then + label = label:lower() + if html_empty_elements[label] then empty = "/" end + end + if all_text or not s_find(text, "^%s*$") then + t_insert(top, unescape(text)) + end + if empty == "/" then -- empty element tag + t_insert(top, setmetatable({tag=label, attr=parseargs(xarg), empty=1},Doc)) + elseif c == "" then -- start tag + top = setmetatable({tag=label, attr=parseargs(xarg)},Doc) + t_insert(stack, top) -- new level + else -- end tag + local toclose = t_remove(stack) -- remove top + top = stack[#stack] + if #stack < 1 then + error("nothing to close with "..label..':'..text) + end + if toclose.tag ~= label then + error("trying to close "..toclose.tag.." with "..label.." "..text) + end + t_insert(top, toclose) + end + end + i = j+1 + end + local text = s_sub(s, i) + if all_text or not s_find(text, "^%s*$") then + t_insert(stack[#stack], unescape(text)) + end + if #stack > 1 then + error("unclosed "..stack[#stack].tag) + end + local res = stack[1] + return is_text(res[1]) and res[2] or res[1] +end + +local function empty(attr) return not attr or not next(attr) end +local function is_element(d) return type(d) == 'table' and d.tag ~= nil end + +-- returns the key,value pair from a table if it has exactly one entry +local function has_one_element(t) + local key,value = next(t) + if next(t,key) ~= nil then return false end + return key,value +end + +local function append_capture(res,tbl) + if not empty(tbl) then -- no point in capturing empty tables... + local key + if tbl._ then -- if $_ was set then it is meant as the top-level key for the captured table + key = tbl._ + tbl._ = nil + if empty(tbl) then return end + end + -- a table with only one pair {[0]=value} shall be reduced to that value + local numkey,val = has_one_element(tbl) + if numkey == 0 then tbl = val end + if key then + res[key] = tbl + else -- otherwise, we append the captured table + t_insert(res,tbl) + end + end +end + +local function make_number(pat) + if pat:find '^%d+$' then -- $1 etc means use this as an array location + pat = tonumber(pat) + end + return pat +end + +local function capture_attrib(res,pat,value) + pat = make_number(pat:sub(2)) + res[pat] = value + return true +end + +local match +function match(d,pat,res,keep_going) + local ret = true + if d == nil then d = '' end --return false end + -- attribute string matching is straight equality, except if the pattern is a $ capture, + -- which always succeeds. + if is_text(d) then + if not is_text(pat) then return false end + if _M.debug then print(d,pat) end + if pat:find '^%$' then + return capture_attrib(res,pat,d) + else + return d == pat + end + else + if _M.debug then print(d.tag,pat.tag) end + -- this is an element node. For a match to succeed, the attributes must + -- match as well. + -- a tagname in the pattern ending with '-' is a wildcard and matches like an attribute + local tagpat = pat.tag:match '^(.-)%-$' + if tagpat then + tagpat = make_number(tagpat) + res[tagpat] = d.tag + end + if d.tag == pat.tag or tagpat then + + if not empty(pat.attr) then + if empty(d.attr) then ret = false + else + for prop,pval in pairs(pat.attr) do + local dval = d.attr[prop] + if not match(dval,pval,res) then ret = false; break end + end + end + end + -- the pattern may have child nodes. We match partially, so that {P1,P2} shall match {X,P1,X,X,P2,..} + if ret and #pat > 0 then + local i,j = 1,1 + local function next_elem() + j = j + 1 -- next child element of data + if is_text(d[j]) then j = j + 1 end + return j <= #d + end + repeat + local p = pat[i] + -- repeated {{<...>}} patterns shall match one or more elements + -- so e.g. {P+} will match {X,X,P,P,X,P,X,X,X} + if is_element(p) and p.repeated then + local found + repeat + local tbl = {} + ret = match(d[j],p,tbl,false) + if ret then + found = false --true + append_capture(res,tbl) + end + until not next_elem() or (found and not ret) + i = i + 1 + else + ret = match(d[j],p,res,false) + if ret then i = i + 1 end + end + until not next_elem() or i > #pat -- run out of elements or patterns to match + -- if every element in our pattern matched ok, then it's been a successful match + if i > #pat then return true end + end + if ret then return true end + else + ret = false + end + -- keep going anyway - look at the children! + if keep_going then + for child in d:childtags() do + ret = match(child,pat,res,keep_going) + if ret then break end + end + end + end + return ret +end + +function Doc:match(pat) + local err + pat,err = template_cache(pat) + if not pat then return nil, err end + _M.walk(pat,false,function(_,d) + if is_text(d[1]) and is_element(d[2]) and is_text(d[3]) and + d[1]:find '%s*{{' and d[3]:find '}}%s*' then + t_remove(d,1) + t_remove(d,2) + d[1].repeated = true + end + end) + + local res = {} + local ret = match(self,pat,res,true) + return res,ret +end + + +return _M + diff --git a/framework/lualib/thirdparty/promise/promise.lua b/framework/lualib/thirdparty/promise/promise.lua new file mode 100644 index 0000000..3879b70 --- /dev/null +++ b/framework/lualib/thirdparty/promise/promise.lua @@ -0,0 +1,291 @@ +-- Port of https://github.com/rhysbrettbowen/promise_impl/blob/master/promise.js +-- and https://github.com/rhysbrettbowen/Aplus + +local queue = {} + +local State = { + PENDING = "pending", + FULFILLED = "fulfilled", + REJECTED = "rejected" +} + +local passthrough = function(x) + return x +end +local errorthrough = function(x) + error(x) +end + +local function callable_table(callback) + local mt = getmetatable(callback) + return type(mt) == "table" and type(mt.__call) == "function" +end + +local function is_callable(value) + local t = type(value) + return t == "function" or (t == "table" and callable_table(value)) +end + +local transition, resolve, run + +local Promise = { + is_promise = true, + state = State.PENDING +} +Promise.mt = {__index = Promise} + +local do_async = function(callback) + if Promise.async then + Promise.async(callback) + else + table.insert(queue, callback) + end +end + +local reject = function(promise, reason) + transition(promise, State.REJECTED, reason) +end + +local fulfill = function(promise, value) + transition(promise, State.FULFILLED, value) +end + +transition = function(promise, state, value) + if + promise.state == state or promise.state ~= State.PENDING or + (state ~= State.FULFILLED and state ~= State.REJECTED) or + value == nil + then + return + end + + promise.state = state + promise.value = value + run(promise) +end + +function Promise:next(on_fulfilled, on_rejected) + local promise = Promise.new() + + table.insert( + self.queue, + { + fulfill = is_callable(on_fulfilled) and on_fulfilled or nil, + reject = is_callable(on_rejected) and on_rejected or nil, + promise = promise + } + ) + + run(self) + + return promise +end + +resolve = function(promise, x) + if promise == x then + reject(promise, "TypeError: cannot resolve a promise with itself") + return + end + + local x_type = type(x) + + if x_type ~= "table" then + fulfill(promise, x) + return + end + + -- x is a promise in the current implementation + if x.is_promise then + -- 2.3.2.1 if x is pending, resolve or reject this promise after completion + if x.state == State.PENDING then + x:next( + function(value) + resolve(promise, value) + end, + function(reason) + reject(promise, reason) + end + ) + return + end + -- if x is not pending, transition promise to x's state and value + transition(promise, x.state, x.value) + return + end + + local called = false + -- 2.3.3.1. Catches errors thrown by __index metatable + local success, reason = + pcall( + function() + local next = x.next + if is_callable(next) then + next( + x, + function(y) + if not called then + resolve(promise, y) + called = true + end + end, + function(r) + if not called then + reject(promise, r) + called = true + end + end + ) + else + fulfill(promise, x) + end + end + ) + + if not success then + if not called then + reject(promise, reason) + end + end +end + +run = function(promise) + if promise.state == State.PENDING then + return + end + + do_async( + function() + -- drain promise.queue while allowing pushes from within callbacks + local q = promise.queue + local i = 0 + while i < #q do + i = i + 1 + local obj = q[i] + local success, result = + pcall( + function() + local success = obj.fulfill or passthrough + local failure = obj.reject or errorthrough + local callback = promise.state == State.FULFILLED and success or failure + return callback(promise.value) + end + ) + + if not success then + reject(obj.promise, result) + else + resolve(obj.promise, result) + end + end + for j = 1, i do + q[j] = nil + end + end + ) +end + +function Promise.new(callback) + local instance = { + queue = {} + } + setmetatable(instance, Promise.mt) + + if callback then + callback( + function(value) + resolve(instance, value) + end, + function(reason) + reject(instance, reason) + end + ) + end + + return instance +end + +function Promise:catch(callback) + return self:next(nil, callback) +end + +function Promise:resolve(value) + fulfill(self, value) +end + +function Promise:reject(reason) + reject(self, reason) +end + +function Promise.update() + while true do + local async = table.remove(queue, 1) + + if not async then + break + end + + async() + end +end + +-- resolve when all promises complete +function Promise.all(...) + local promises = {...} + local results = {} + local state = State.FULFILLED + local remaining = #promises + + local promise = Promise.new() + + local check_finished = function() + if remaining > 0 then + return + end + transition(promise, state, results) + end + + for i, p in ipairs(promises) do + p:next( + function(value) + results[i] = value + remaining = remaining - 1 + check_finished() + end, + function(value) + results[i] = value + remaining = remaining - 1 + state = State.REJECTED + check_finished() + end + ) + end + + check_finished() + + return promise +end + +-- resolve with first promise to complete +function Promise.race(...) + local promises = {...} + local promise = Promise.new() + + Promise.all(...):next( + nil, + function(value) + reject(promise, value) + end + ) + + local success = function(value) + fulfill(promise, value) + end + + for _, p in ipairs(promises) do + p:next(success) + end + + return promise +end + +return Promise diff --git a/framework/lualib/thirdparty/router.lua b/framework/lualib/thirdparty/router.lua new file mode 100644 index 0000000..d787770 --- /dev/null +++ b/framework/lualib/thirdparty/router.lua @@ -0,0 +1,192 @@ +-- https://github.com/APItools/router.lua +local router = { + _VERSION = "router.lua v2.1.0", + _DESCRIPTION = "A simple router for Lua", + _LICENSE = [[ + MIT LICENSE + * Copyright (c) 2013 Enrique García Cota + * Copyright (c) 2013 Raimon Grau + * Copyright (c) 2015 Lloyd Zhou + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ]] +} + +local COLON_BYTE = string.byte(":", 1) +local WILDCARD_BYTE = string.byte("*", 1) +local HTTP_METHODS = {"get", "post", "put", "patch", "delete", "trace", "connect", "options", "head"} + +local function match_one_path(node, path, f) + for token in path:gmatch("[^/.]+") do + if WILDCARD_BYTE == token:byte(1) then + node["WILDCARD"] = {["LEAF"] = f, ["TOKEN"] = token:sub(2)} + return + end + if COLON_BYTE == token:byte(1) then -- if match the ":", store the param_name in "TOKEN" array. + node["TOKEN"] = node["TOKEN"] or {} + token = token:sub(2) + node = node["TOKEN"] + end + node[token] = node[token] or {} + node = node[token] + end + node["LEAF"] = f +end + +local function resolve(path, node, params) + local _, _, current_token, rest = path:find("([^/.]+)(.*)") + if not current_token then + if node["WILDCARD"] then + params[node["WILDCARD"]["TOKEN"]] = "" + return node["WILDCARD"]["LEAF"], params + end + return node["LEAF"], params + end + + if node["WILDCARD"] then + params[node["WILDCARD"]["TOKEN"]] = current_token .. rest + return node["WILDCARD"]["LEAF"], params + end + + if node[current_token] then + local f, bindings = resolve(rest, node[current_token], params) + if f then + return f, bindings + end + end + + for param_name, child_node in pairs(node["TOKEN"] or {}) do + local param_value = params[param_name] + params[param_name] = current_token or param_value -- store the value in params, resolve tail path + + local f, bindings = resolve(rest, child_node, params) + if f then + return f, bindings + end + + params[param_name] = param_value -- reset the params table. + end + + return false +end + +local function merge(destination, origin, visited) + if type(origin) ~= "table" then + return origin + end + if visited[origin] then + return visited[origin] + end + if destination == nil then + destination = {} + end + + for k, v in pairs(origin) do + k = merge(nil, k, visited) -- makes a copy of k + if destination[k] == nil then + destination[k] = merge(nil, v, visited) + end + end + + return destination +end + +local function merge_params(...) + local params_list = {...} + local result, visited = {}, {} + + for i = 1, #params_list do + merge(result, params_list[i], visited) + end + + return result +end + +------------------------------ INSTANCE METHODS ------------------------------------ +local Router = {} + +function Router:resolve(method, path, ...) + local node = self._tree[method] + if not node then + return nil, ("Unknown method: %s"):format(tostring(method)) + end + return resolve(path, node, merge_params(...)) +end + +function Router:execute(method, path, ...) + local f, params = self:resolve(method, path, ...) + if not f then + return nil, ("Could not resolve %s %s - %s"):format(tostring(method), tostring(path), tostring(params)) + end + return true, f(params) +end + +function Router:match(method, path, fun) + if type(method) == "string" then -- always make the method to table. + method = {method} + end + + local parsed_methods = {} + for k, v in pairs(method) do + if type(v) == "string" then + -- convert shorthand methods into longhand + if parsed_methods[v] == nil then + parsed_methods[v] = {} + end + parsed_methods[v][path] = fun + else + -- pass methods already in longhand format onwards + parsed_methods[k] = v + end + end + + for m, routes in pairs(parsed_methods) do + for p, f in pairs(routes) do + if not self._tree[m] then + self._tree[m] = {} + end + match_one_path(self._tree[m], p, f) + end + end +end + +for _, method in ipairs(HTTP_METHODS) do + Router[method] = function(self, path, f) -- Router.get = function(self, path, f) + self:match(method:upper(), path, f) -- return self:match('GET', path, f) + end -- end +end + +Router["any"] = function(self, path, f) -- match any method + for _, method in ipairs(HTTP_METHODS) do + self:match( + method:upper(), + path, + function(params) + return f(params, method) + end + ) + end +end + +local router_mt = {__index = Router} + +------------------------------ PUBLIC INTERFACE ------------------------------------ +router.new = function() + return setmetatable({_tree = {}}, router_mt) +end + +return router diff --git a/framework/lualib/thirdparty/rubble/rubble.lua b/framework/lualib/thirdparty/rubble/rubble.lua new file mode 100644 index 0000000..be33291 --- /dev/null +++ b/framework/lualib/thirdparty/rubble/rubble.lua @@ -0,0 +1,93 @@ +-- --[[ +-- lua-rubble: Rust-like struct-trait-impl system +-- https://github.com/evolbug/lua-rubble +-- ]] + +-- function struct(members) +-- assert(type(members) == 'table', 'must use table for call') +-- return setmetatable({ +-- impls = {}, +-- traits = {}, +-- }, { +-- __index = { +-- impl = function(self, methods) +-- assert(type(methods) == 'table', 'must use table for methods') +-- assert(#methods == 0, 'only named members allowed') +-- for k, v in pairs(methods) do +-- assert(type(k) == 'string', 'name must be a string') +-- assert(type(v) == 'function', 'not a method') +-- assert(not self.impls[k], 'method ' .. k .. ' already defined') +-- self.impls[k] = v +-- end +-- end, +-- }, + +-- __call = function(self, init) +-- assert(type(init) == 'table', 'must use table for init') +-- assert(#init == 0, 'only named members allowed') +-- for key, value in pairs(init) do +-- assert(members[key], 'initializing nonexistent field ' .. key) +-- assert(type(value) == members[key], +-- "type mismatch for member " .. key .. ' expected:' .. members[key] .. ' got:' .. type(value)) +-- end +-- return setmetatable(init, { +-- __index = self.impls, +-- __newindex = function(self, key, value) +-- assert(members[key], "setting nonexistent member " .. key) +-- assert(type(value) == members[key], +-- "type mismatch for member " .. key .. ' expected:' .. members[key] .. ' got:' .. type(value)) +-- self[key] = value +-- end, +-- }) +-- end, + +-- __newindex = function(self, key, value) +-- error('struct definitions are immutable') +-- end, +-- }) +-- end + +-- function trait(members) +-- assert(type(members) == 'table', 'must use table for call') +-- return setmetatable(members, { +-- __index = { +-- impl = function(self, struct) +-- return function(methods) +-- assert(type(methods) == 'table', 'must use table for methods') +-- assert(#methods == 0, 'only named members allowed') +-- for k, v in pairs(self) do +-- assert(methods[k], 'must implement method ' .. k) +-- assert(type(methods[k]) == 'function', 'method ' .. k .. ' not a function') +-- end +-- for k, v in pairs(methods) do +-- assert(type(k) == 'string', 'name must be a string') +-- assert(type(v) == 'function', 'not a method') +-- assert(self[k], 'method not specified in trait') +-- assert(not struct.impls[k], 'method ' .. k .. ' already defined') +-- struct.impls[k] = v +-- end +-- struct.traits[self] = true +-- end +-- end, +-- }, +-- __call = function(self, init) +-- assert(type(init) == 'table', 'must use table for init') +-- assert(#init == 0, 'only named members allowed') +-- for k, v in pairs(init) do +-- assert(members[k], 'initializing nonexistent field') +-- end +-- return setmetatable(init, { +-- __index = self.impls, +-- __newindex = function(self, key, value) +-- assert(members[key], "setting nonexistent member " .. key) +-- assert(type(value) == members[key], "type mismatch for member") +-- self[key] = value +-- end, +-- }) +-- end, + +-- __newindex = function(self, key, value) +-- error('trait definitions are immutable') +-- end, +-- }) +-- end diff --git a/framework/lualib/thirdparty/rx/aliases.lua b/framework/lualib/thirdparty/rx/aliases.lua new file mode 100755 index 0000000..762f72e --- /dev/null +++ b/framework/lualib/thirdparty/rx/aliases.lua @@ -0,0 +1,4 @@ +local Observable = require "rx.observable" + +Observable.wrap = Observable.buffer +Observable["repeat"] = Observable.replicate diff --git a/framework/lualib/thirdparty/rx/init.lua b/framework/lualib/thirdparty/rx/init.lua new file mode 100755 index 0000000..62cb4e0 --- /dev/null +++ b/framework/lualib/thirdparty/rx/init.lua @@ -0,0 +1,2487 @@ +-- RxLua v0.0.3 +-- https://github.com/bjornbytes/rxlua +-- MIT License +local util = {} + +util.pack = table.pack or function(...) + return { + n = select("#", ...), + ..., + } +end +util.unpack = table.unpack or _G.unpack +util.eq = function(x, y) + return x == y +end +util.noop = function() +end +util.identity = function(x) + return x +end +util.constant = function(x) + return function() + return x + end +end +util.isa = function(object, class) + return type(object) == "table" and getmetatable(object).__index == class +end +util.tryWithObserver = function(observer, fn, ...) + local success, result = pcall(fn, ...) + if not success then + observer:onError(result) + end + return success, result +end + +--- @class Subscription +-- @description A handle representing the link between an Observer and an Observable, as well as any +-- work required to clean up after the Observable completes or the Observer unsubscribes. +local Subscription = {} +Subscription.__index = Subscription +Subscription.__tostring = util.constant("Subscription") + +--- Creates a new Subscription. +-- @arg {function=} action - The action to run when the subscription is unsubscribed. It will only +-- be run once. +-- @returns {Subscription} +function Subscription.create(action) + local self = { + action = action or util.noop, + unsubscribed = false, + } + + return setmetatable(self, Subscription) +end + +--- Unsubscribes the subscription, performing any necessary cleanup work. +function Subscription:unsubscribe() + if self.unsubscribed then + return + end + self.action(self) + self.unsubscribed = true +end + +--- @class Observer +-- @description Observers are simple objects that receive values from Observables. +local Observer = {} +Observer.__index = Observer +Observer.__tostring = util.constant("Observer") + +--- Creates a new Observer. +-- @arg {function=} onNext - Called when the Observable produces a value. +-- @arg {function=} onError - Called when the Observable terminates due to an error. +-- @arg {function=} onCompleted - Called when the Observable completes normally. +-- @returns {Observer} +function Observer.create(onNext, onError, onCompleted) + local self = { + _onNext = onNext or util.noop, + _onError = onError or error, + _onCompleted = onCompleted or util.noop, + stopped = false, + } + + return setmetatable(self, Observer) +end + +--- Pushes zero or more values to the Observer. +-- @arg {*...} values +function Observer:onNext(...) + if not self.stopped then + self._onNext(...) + end +end + +--- Notify the Observer that an error has occurred. +-- @arg {string=} message - A string describing what went wrong. +function Observer:onError(message) + if not self.stopped then + self.stopped = true + self._onError(message) + end +end + +--- Notify the Observer that the sequence has completed and will produce no more values. +function Observer:onCompleted() + if not self.stopped then + self.stopped = true + self._onCompleted() + end +end + +--- @class Observable +-- @description Observables push values to Observers. +local Observable = {} +Observable.__index = Observable +Observable.__tostring = util.constant("Observable") + +--- Creates a new Observable. +-- @arg {function} subscribe - The subscription function that produces values. +-- @returns {Observable} +function Observable.create(subscribe) + local self = { + _subscribe = subscribe, + } + + return setmetatable(self, Observable) +end + +--- Shorthand for creating an Observer and passing it to this Observable's subscription function. +-- @arg {function} onNext - Called when the Observable produces a value. +-- @arg {function} onError - Called when the Observable terminates due to an error. +-- @arg {function} onCompleted - Called when the Observable completes normally. +function Observable:subscribe(onNext, onError, onCompleted) + if type(onNext) == "table" then + return self._subscribe(onNext) + else + return self._subscribe(Observer.create(onNext, onError, onCompleted)) + end +end + +--- Returns an Observable that immediately completes without producing a value. +function Observable.empty() + return Observable.create(function(observer) + observer:onCompleted() + end) +end + +--- Returns an Observable that never produces values and never completes. +function Observable.never() + return Observable.create(function() + end) +end + +--- Returns an Observable that immediately produces an error. +function Observable.throw(message) + return Observable.create(function(observer) + observer:onError(message) + end) +end + +--- Creates an Observable that produces a set of values. +-- @arg {*...} values +-- @returns {Observable} +function Observable.of(...) + local args = {...} + local argCount = select("#", ...) + return Observable.create(function(observer) + for i = 1, argCount do + observer:onNext(args[i]) + end + + observer:onCompleted() + end) +end + +--- Creates an Observable that produces a range of values in a manner similar to a Lua for loop. +-- @arg {number} initial - The first value of the range, or the upper limit if no other arguments +-- are specified. +-- @arg {number=} limit - The second value of the range. +-- @arg {number=1} step - An amount to increment the value by each iteration. +-- @returns {Observable} +function Observable.fromRange(initial, limit, step) + if not limit and not step then + initial, limit = 1, initial + end + + step = step or 1 + + return Observable.create(function(observer) + for i = initial, limit, step do + observer:onNext(i) + end + + observer:onCompleted() + end) +end + +--- Creates an Observable that produces values from a table. +-- @arg {table} table - The table used to create the Observable. +-- @arg {function=pairs} iterator - An iterator used to iterate the table, e.g. pairs or ipairs. +-- @arg {boolean} keys - Whether or not to also emit the keys of the table. +-- @returns {Observable} +function Observable.fromTable(t, iterator, keys) + iterator = iterator or pairs + return Observable.create(function(observer) + for key, value in iterator(t) do + observer:onNext(value, keys and key or nil) + end + + observer:onCompleted() + end) +end + +--- Creates an Observable that produces values when the specified coroutine yields. +-- @arg {thread|function} fn - A coroutine or function to use to generate values. Note that if a +-- coroutine is used, the values it yields will be shared by all +-- subscribed Observers (influenced by the Scheduler), whereas a new +-- coroutine will be created for each Observer when a function is used. +-- @returns {Observable} +function Observable.fromCoroutine(fn, scheduler) + return Observable.create(function(observer) + local thread = type(fn) == "function" and coroutine.create(fn) or fn + return scheduler:schedule(function() + while not observer.stopped do + local success, value = coroutine.resume(thread) + + if success then + observer:onNext(value) + else + return observer:onError(value) + end + + if coroutine.status(thread) == "dead" then + return observer:onCompleted() + end + + coroutine.yield() + end + end) + end) +end + +--- Creates an Observable that produces values from a file, line by line. +-- @arg {string} filename - The name of the file used to create the Observable +-- @returns {Observable} +function Observable.fromFileByLine(filename) + return Observable.create(function(observer) + local file = io.open(filename, "r") + if file then + file:close() + + for line in io.lines(filename) do + observer:onNext(line) + end + + return observer:onCompleted() + else + return observer:onError(filename) + end + end) +end + +--- Creates an Observable that creates a new Observable for each observer using a factory function. +-- @arg {function} factory - A function that returns an Observable. +-- @returns {Observable} +function Observable.defer(fn) + if not fn or type(fn) ~= "function" then + error("Expected a function") + end + + return setmetatable({ + subscribe = function(_, ...) + local observable = fn() + return observable:subscribe(...) + end, + }, Observable) +end + +--- Returns an Observable that repeats a value a specified number of times. +-- @arg {*} value - The value to repeat. +-- @arg {number=} count - The number of times to repeat the value. If left unspecified, the value +-- is repeated an infinite number of times. +-- @returns {Observable} +function Observable.replicate(value, count) + return Observable.create(function(observer) + while count == nil or count > 0 do + observer:onNext(value) + if count then + count = count - 1 + end + end + observer:onCompleted() + end) +end + +--- Subscribes to this Observable and prints values it produces. +-- @arg {string=} name - Prefixes the printed messages with a name. +-- @arg {function=tostring} formatter - A function that formats one or more values to be printed. +function Observable:dump(name, formatter) + name = name and (name .. " ") or "" + + local onNext + if formatter then + onNext = function(...) + print(name .. "onNext: " .. formatter(...)) + end + else + onNext = function(...) + print(name .. "onNext: ", ...) + end + end + local onError = function(e) + print(name .. "onError: " .. e) + end + local onCompleted = function() + print(name .. "onCompleted") + end + + return self:subscribe(onNext, onError, onCompleted) +end + +--- Determine whether all items emitted by an Observable meet some criteria. +-- @arg {function=identity} predicate - The predicate used to evaluate objects. +function Observable:all(predicate) + predicate = predicate or util.identity + + return Observable.create(function(observer) + local subscription + local function onNext(...) + util.tryWithObserver(observer, function(...) + if not predicate(...) then + observer:onNext(false) + if subscription then + subscription:unsubscribe() + end + observer:onCompleted() + end + end, ...) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + observer:onNext(true) + return observer:onCompleted() + end + + subscription = self:subscribe(onNext, onError, onCompleted) + return subscription + end) +end + +--- Given a set of Observables, produces values from only the first one to produce a value. +-- @arg {Observable...} observables +-- @returns {Observable} +function Observable.amb(a, b, ...) + if not a or not b then + return a + end + + return Observable.create(function(observer) + local subscriptionA, subscriptionB + + local function onNextA(...) + if subscriptionB then + subscriptionB:unsubscribe() + end + observer:onNext(...) + end + + local function onErrorA(e) + if subscriptionB then + subscriptionB:unsubscribe() + end + observer:onError(e) + end + + local function onCompletedA() + if subscriptionB then + subscriptionB:unsubscribe() + end + observer:onCompleted() + end + + local function onNextB(...) + if subscriptionA then + subscriptionA:unsubscribe() + end + observer:onNext(...) + end + + local function onErrorB(e) + if subscriptionA then + subscriptionA:unsubscribe() + end + observer:onError(e) + end + + local function onCompletedB() + if subscriptionA then + subscriptionA:unsubscribe() + end + observer:onCompleted() + end + + subscriptionA = a:subscribe(onNextA, onErrorA, onCompletedA) + subscriptionB = b:subscribe(onNextB, onErrorB, onCompletedB) + + return Subscription.create(function() + subscriptionA:unsubscribe() + subscriptionB:unsubscribe() + end) + end):amb(...) +end + +--- Returns an Observable that produces the average of all values produced by the original. +-- @returns {Observable} +function Observable:average() + return Observable.create(function(observer) + local sum, count = 0, 0 + + local function onNext(value) + sum = sum + value + count = count + 1 + end + + local function onError(e) + observer:onError(e) + end + + local function onCompleted() + if count > 0 then + observer:onNext(sum / count) + end + + observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns an Observable that buffers values from the original and produces them as multiple +-- values. +-- @arg {number} size - The size of the buffer. +function Observable:buffer(size) + if not size or type(size) ~= "number" then + error("Expected a number") + end + + return Observable.create(function(observer) + local buffer = {} + + local function emit() + if #buffer > 0 then + observer:onNext(util.unpack(buffer)) + buffer = {} + end + end + + local function onNext(...) + local values = {...} + for i = 1, #values do + table.insert(buffer, values[i]) + if #buffer >= size then + emit() + end + end + end + + local function onError(message) + emit() + return observer:onError(message) + end + + local function onCompleted() + emit() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns an Observable that intercepts any errors from the previous and replace them with values +-- produced by a new Observable. +-- @arg {function|Observable} handler - An Observable or a function that returns an Observable to +-- replace the source Observable in the event of an error. +-- @returns {Observable} +function Observable:catch(handler) + handler = handler and (type(handler) == "function" and handler or util.constant(handler)) + + return Observable.create(function(observer) + local subscription + + local function onNext(...) + return observer:onNext(...) + end + + local function onError(e) + if not handler then + if subscription then + subscription:unsubscribe() + end + return observer:onCompleted() + end + + local success, _continue = pcall(handler, e) + if success and _continue then + if subscription then + subscription:unsubscribe() + end + _continue:subscribe(observer) + else + observer:onError(success and e or _continue) + end + end + + local function onCompleted() + observer:onCompleted() + end + + subscription = self:subscribe(onNext, onError, onCompleted) + return subscription + end) +end + +--- Returns a new Observable that runs a combinator function on the most recent values from a set +-- of Observables whenever any of them produce a new value. The results of the combinator function +-- are produced by the new Observable. +-- @arg {Observable...} observables - One or more Observables to combine. +-- @arg {function} combinator - A function that combines the latest result from each Observable and +-- returns a single value. +-- @returns {Observable} +function Observable:combineLatest(...) + local sources = {...} + local combinator = table.remove(sources) + if type(combinator) ~= "function" then + table.insert(sources, combinator) + combinator = function(...) + return ... + end + end + table.insert(sources, 1, self) + + return Observable.create(function(observer) + local latest = {} + local pending = {util.unpack(sources)} + local completed = {} + local subscription = {} + + local function onNext(i) + return function(value) + latest[i] = value + pending[i] = nil + + if not next(pending) then + util.tryWithObserver(observer, function() + observer:onNext(combinator(util.unpack(latest))) + end) + end + end + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted(i) + return function() + table.insert(completed, i) + + if #completed == #sources then + observer:onCompleted() + end + end + end + + for i = 1, #sources do + subscription[i] = sources[i]:subscribe(onNext(i), onError, onCompleted(i)) + end + + return Subscription.create(function() + for i = 1, #sources do + if subscription[i] then + subscription[i]:unsubscribe() + end + end + end) + end) +end + +--- Returns a new Observable that produces the values of the first with falsy values removed. +-- @returns {Observable} +function Observable:compact() + return self:filter(util.identity) +end + +--- Returns a new Observable that produces the values produced by all the specified Observables in +-- the order they are specified. +-- @arg {Observable...} sources - The Observables to concatenate. +-- @returns {Observable} +function Observable:concat(other, ...) + if not other then + return self + end + + local others = {...} + + return Observable.create(function(observer) + local function onNext(...) + return observer:onNext(...) + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + local function chain() + return other:concat(util.unpack(others)):subscribe(onNext, onError, onCompleted) + end + + return self:subscribe(onNext, onError, chain) + end) +end + +--- Returns a new Observable that produces a single boolean value representing whether or not the +-- specified value was produced by the original. +-- @arg {*} value - The value to search for. == is used for equality testing. +-- @returns {Observable} +function Observable:contains(value) + return Observable.create(function(observer) + local subscription + + local function onNext(...) + local args = util.pack(...) + + if #args == 0 and value == nil then + observer:onNext(true) + if subscription then + subscription:unsubscribe() + end + return observer:onCompleted() + end + + for i = 1, #args do + if args[i] == value then + observer:onNext(true) + if subscription then + subscription:unsubscribe() + end + return observer:onCompleted() + end + end + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + observer:onNext(false) + return observer:onCompleted() + end + + subscription = self:subscribe(onNext, onError, onCompleted) + return subscription + end) +end + +--- Returns an Observable that produces a single value representing the number of values produced +-- by the source value that satisfy an optional predicate. +-- @arg {function=} predicate - The predicate used to match values. +function Observable:count(predicate) + predicate = predicate or util.constant(true) + + return Observable.create(function(observer) + local count = 0 + + local function onNext(...) + util.tryWithObserver(observer, function(...) + if predicate(...) then + count = count + 1 + end + end, ...) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + observer:onNext(count) + observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns a new throttled Observable that waits to produce values until a timeout has expired, at +-- which point it produces the latest value from the source Observable. Whenever the source +-- Observable produces a value, the timeout is reset. +-- @arg {number|function} time - An amount in milliseconds to wait before producing the last value. +-- @arg {Scheduler} scheduler - The scheduler to run the Observable on. +-- @returns {Observable} +function Observable:debounce(time, scheduler) + time = time or 0 + + return Observable.create(function(observer) + local debounced = {} + + local function wrap(key) + return function(...) + if debounced[key] then + debounced[key]:unsubscribe() + end + + local values = util.pack(...) + + debounced[key] = scheduler:schedule(function() + return observer[key](observer, util.unpack(values)) + end, time) + end + end + + local subscription = self:subscribe(wrap("onNext"), wrap("onError"), wrap("onCompleted")) + + return Subscription.create(function() + if subscription then + subscription:unsubscribe() + end + for _, timeout in pairs(debounced) do + timeout:unsubscribe() + end + end) + end) +end + +--- Returns a new Observable that produces a default set of items if the source Observable produces +-- no values. +-- @arg {*...} values - Zero or more values to produce if the source completes without emitting +-- anything. +-- @returns {Observable} +function Observable:defaultIfEmpty(...) + local defaults = util.pack(...) + + return Observable.create(function(observer) + local hasValue = false + + local function onNext(...) + hasValue = true + observer:onNext(...) + end + + local function onError(e) + observer:onError(e) + end + + local function onCompleted() + if not hasValue then + observer:onNext(util.unpack(defaults)) + end + + observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns a new Observable that produces the values of the original delayed by a time period. +-- @arg {number|function} time - An amount in milliseconds to delay by, or a function which returns +-- this value. +-- @arg {Scheduler} scheduler - The scheduler to run the Observable on. +-- @returns {Observable} +function Observable:delay(time, scheduler) + time = type(time) ~= "function" and util.constant(time) or time + + return Observable.create(function(observer) + local actions = {} + + local function delay(key) + return function(...) + local arg = util.pack(...) + local handle = scheduler:schedule(function() + observer[key](observer, util.unpack(arg)) + end, time()) + table.insert(actions, handle) + end + end + + local subscription = self:subscribe(delay("onNext"), delay("onError"), delay("onCompleted")) + + return Subscription.create(function() + if subscription then + subscription:unsubscribe() + end + for i = 1, #actions do + actions[i]:unsubscribe() + end + end) + end) +end + +--- Returns a new Observable that produces the values from the original with duplicates removed. +-- @returns {Observable} +function Observable:distinct() + return Observable.create(function(observer) + local values = {} + + local function onNext(x) + if not values[x] then + observer:onNext(x) + end + + values[x] = true + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns an Observable that only produces values from the original if they are different from +-- the previous value. +-- @arg {function} comparator - A function used to compare 2 values. If unspecified, == is used. +-- @returns {Observable} +function Observable:distinctUntilChanged(comparator) + comparator = comparator or util.eq + + return Observable.create(function(observer) + local first = true + local currentValue = nil + + local function onNext(value, ...) + local values = util.pack(...) + util.tryWithObserver(observer, function() + if first or not comparator(value, currentValue) then + observer:onNext(value, util.unpack(values)) + currentValue = value + first = false + end + end) + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns an Observable that produces the nth element produced by the source Observable. +-- @arg {number} index - The index of the item, with an index of 1 representing the first. +-- @returns {Observable} +function Observable:elementAt(index) + if not index or type(index) ~= "number" then + error("Expected a number") + end + + return Observable.create(function(observer) + local subscription + local i = 1 + + local function onNext(...) + if i == index then + observer:onNext(...) + observer:onCompleted() + if subscription then + subscription:unsubscribe() + end + else + i = i + 1 + end + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + subscription = self:subscribe(onNext, onError, onCompleted) + return subscription + end) +end + +--- Returns a new Observable that only produces values of the first that satisfy a predicate. +-- @arg {function} predicate - The predicate used to filter values. +-- @returns {Observable} +function Observable:filter(predicate) + predicate = predicate or util.identity + + return Observable.create(function(observer) + local function onNext(...) + util.tryWithObserver(observer, function(...) + if predicate(...) then + return observer:onNext(...) + end + end, ...) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns a new Observable that produces the first value of the original that satisfies a +-- predicate. +-- @arg {function} predicate - The predicate used to find a value. +function Observable:find(predicate) + predicate = predicate or util.identity + + return Observable.create(function(observer) + local subscription + + local function onNext(...) + util.tryWithObserver(observer, function(...) + if predicate(...) then + observer:onNext(...) + if subscription then + subscription:unsubscribe() + end + return observer:onCompleted() + end + end, ...) + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + subscription = self:subscribe(onNext, onError, onCompleted) + return subscription + end) +end + +--- Returns a new Observable that only produces the first result of the original. +-- @returns {Observable} +function Observable:first() + return self:take(1) +end + +--- Returns a new Observable that transform the items emitted by an Observable into Observables, +-- then flatten the emissions from those into a single Observable +-- @arg {function} callback - The function to transform values from the original Observable. +-- @returns {Observable} +function Observable:flatMap(callback) + callback = callback or util.identity + return self:map(callback):flatten() +end + +--- Returns a new Observable that uses a callback to create Observables from the values produced by +-- the source, then produces values from the most recent of these Observables. +-- @arg {function=identity} callback - The function used to convert values to Observables. +-- @returns {Observable} +function Observable:flatMapLatest(callback) + callback = callback or util.identity + return Observable.create(function(observer) + local innerSubscription + + local function onNext(...) + observer:onNext(...) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + local function subscribeInner(...) + if innerSubscription then + innerSubscription:unsubscribe() + end + + return util.tryWithObserver(observer, function(...) + innerSubscription = callback(...):subscribe(onNext, onError) + end, ...) + end + + local subscription = self:subscribe(subscribeInner, onError, onCompleted) + return Subscription.create(function() + if innerSubscription then + innerSubscription:unsubscribe() + end + + if subscription then + subscription:unsubscribe() + end + end) + end) +end + +--- Returns a new Observable that subscribes to the Observables produced by the original and +-- produces their values. +-- @returns {Observable} +function Observable:flatten() + return Observable.create(function(observer) + local subscriptions = {} + local remaining = 1 + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + remaining = remaining - 1 + if remaining == 0 then + return observer:onCompleted() + end + end + + local function onNext(observable) + local function innerOnNext(...) + observer:onNext(...) + end + + remaining = remaining + 1 + local subscription = observable:subscribe(innerOnNext, onError, onCompleted) + subscriptions[#subscriptions + 1] = subscription + end + + subscriptions[#subscriptions + 1] = self:subscribe(onNext, onError, onCompleted) + return Subscription.create(function() + for i = 1, #subscriptions do + subscriptions[i]:unsubscribe() + end + end) + end) +end + +--- Returns an Observable that terminates when the source terminates but does not produce any +-- elements. +-- @returns {Observable} +function Observable:ignoreElements() + return Observable.create(function(observer) + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(nil, onError, onCompleted) + end) +end + +--- Returns a new Observable that only produces the last result of the original. +-- @returns {Observable} +function Observable:last() + return Observable.create(function(observer) + local value + local empty = true + + local function onNext(...) + value = {...} + empty = false + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + if not empty then + observer:onNext(util.unpack(value or {})) + end + + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns a new Observable that produces the values of the original transformed by a function. +-- @arg {function} callback - The function to transform values from the original Observable. +-- @returns {Observable} +function Observable:map(callback) + return Observable.create(function(observer) + callback = callback or util.identity + + local function onNext(...) + return util.tryWithObserver(observer, function(...) + return observer:onNext(callback(...)) + end, ...) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns a new Observable that produces the maximum value produced by the original. +-- @returns {Observable} +function Observable:max() + return self:reduce(math.max) +end + +--- Returns a new Observable that produces the values produced by all the specified Observables in +-- the order they are produced. +-- @arg {Observable...} sources - One or more Observables to merge. +-- @returns {Observable} +function Observable:merge(...) + local sources = {...} + table.insert(sources, 1, self) + + return Observable.create(function(observer) + local completed = {} + local subscriptions = {} + + local function onNext(...) + return observer:onNext(...) + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted(i) + return function() + table.insert(completed, i) + + if #completed == #sources then + observer:onCompleted() + end + end + end + + for i = 1, #sources do + subscriptions[i] = sources[i]:subscribe(onNext, onError, onCompleted(i)) + end + + return Subscription.create(function() + for i = 1, #sources do + if subscriptions[i] then + subscriptions[i]:unsubscribe() + end + end + end) + end) +end + +--- Returns a new Observable that produces the minimum value produced by the original. +-- @returns {Observable} +function Observable:min() + return self:reduce(math.min) +end + +--- Returns an Observable that produces the values of the original inside tables. +-- @returns {Observable} +function Observable:pack() + return self:map(util.pack) +end + +--- Returns two Observables: one that produces values for which the predicate returns truthy for, +-- and another that produces values for which the predicate returns falsy. +-- @arg {function} predicate - The predicate used to partition the values. +-- @returns {Observable} +-- @returns {Observable} +function Observable:partition(predicate) + return self:filter(predicate), self:reject(predicate) +end + +--- Returns a new Observable that produces values computed by extracting the given keys from the +-- tables produced by the original. +-- @arg {string...} keys - The key to extract from the table. Multiple keys can be specified to +-- recursively pluck values from nested tables. +-- @returns {Observable} +function Observable:pluck(key, ...) + if not key then + return self + end + + if type(key) ~= "string" and type(key) ~= "number" then + return Observable.throw("pluck key must be a string") + end + + return Observable.create(function(observer) + local function onNext(t) + return observer:onNext(t[key]) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end):pluck(...) +end + +--- Returns a new Observable that produces a single value computed by accumulating the results of +-- running a function on each value produced by the original Observable. +-- @arg {function} accumulator - Accumulates the values of the original Observable. Will be passed +-- the return value of the last call as the first argument and the +-- current values as the rest of the arguments. +-- @arg {*} seed - A value to pass to the accumulator the first time it is run. +-- @returns {Observable} +function Observable:reduce(accumulator, seed) + return Observable.create(function(observer) + local result = seed + local first = true + + local function onNext(...) + if first and seed == nil then + result = ... + first = false + else + return util.tryWithObserver(observer, function(...) + result = accumulator(result, ...) + end, ...) + end + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + observer:onNext(result) + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns a new Observable that produces values from the original which do not satisfy a +-- predicate. +-- @arg {function} predicate - The predicate used to reject values. +-- @returns {Observable} +function Observable:reject(predicate) + predicate = predicate or util.identity + + return Observable.create(function(observer) + local function onNext(...) + util.tryWithObserver(observer, function(...) + if not predicate(...) then + return observer:onNext(...) + end + end, ...) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns an Observable that restarts in the event of an error. +-- @arg {number=} count - The maximum number of times to retry. If left unspecified, an infinite +-- number of retries will be attempted. +-- @returns {Observable} +function Observable:retry(count) + return Observable.create(function(observer) + local subscription + local retries = 0 + + local function onNext(...) + return observer:onNext(...) + end + + local function onCompleted() + return observer:onCompleted() + end + + local function onError(message) + if subscription then + subscription:unsubscribe() + end + + retries = retries + 1 + if count and retries > count then + return observer:onError(message) + end + + subscription = self:subscribe(onNext, onError, onCompleted) + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns a new Observable that produces its most recent value every time the specified observable +-- produces a value. +-- @arg {Observable} sampler - The Observable that is used to sample values from this Observable. +-- @returns {Observable} +function Observable:sample(sampler) + if not sampler then + error("Expected an Observable") + end + + return Observable.create(function(observer) + local latest = {} + + local function setLatest(...) + latest = util.pack(...) + end + + local function onNext() + if #latest > 0 then + return observer:onNext(util.unpack(latest)) + end + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + local sourceSubscription = self:subscribe(setLatest, onError) + local sampleSubscription = sampler:subscribe(onNext, onError, onCompleted) + + return Subscription.create(function() + if sourceSubscription then + sourceSubscription:unsubscribe() + end + if sampleSubscription then + sampleSubscription:unsubscribe() + end + end) + end) +end + +--- Returns a new Observable that produces values computed by accumulating the results of running a +-- function on each value produced by the original Observable. +-- @arg {function} accumulator - Accumulates the values of the original Observable. Will be passed +-- the return value of the last call as the first argument and the +-- current values as the rest of the arguments. Each value returned +-- from this function will be emitted by the Observable. +-- @arg {*} seed - A value to pass to the accumulator the first time it is run. +-- @returns {Observable} +function Observable:scan(accumulator, seed) + return Observable.create(function(observer) + local result = seed + local first = true + + local function onNext(...) + if first and seed == nil then + result = ... + first = false + else + return util.tryWithObserver(observer, function(...) + result = accumulator(result, ...) + observer:onNext(result) + end, ...) + end + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns a new Observable that skips over a specified number of values produced by the original +-- and produces the rest. +-- @arg {number=1} n - The number of values to ignore. +-- @returns {Observable} +function Observable:skip(n) + n = n or 1 + + return Observable.create(function(observer) + local i = 1 + + local function onNext(...) + if i > n then + observer:onNext(...) + else + i = i + 1 + end + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns an Observable that omits a specified number of values from the end of the original +-- Observable. +-- @arg {number} count - The number of items to omit from the end. +-- @returns {Observable} +function Observable:skipLast(count) + if not count or type(count) ~= "number" then + error("Expected a number") + end + + local buffer = {} + return Observable.create(function(observer) + local function emit() + if #buffer > count and buffer[1] then + local values = table.remove(buffer, 1) + observer:onNext(util.unpack(values)) + end + end + + local function onNext(...) + emit() + table.insert(buffer, util.pack(...)) + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + emit() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns a new Observable that skips over values produced by the original until the specified +-- Observable produces a value. +-- @arg {Observable} other - The Observable that triggers the production of values. +-- @returns {Observable} +function Observable:skipUntil(other) + return Observable.create(function(observer) + local triggered = false + local function trigger() + triggered = true + end + + other:subscribe(trigger, trigger, trigger) + + local function onNext(...) + if triggered then + observer:onNext(...) + end + end + + local function onError() + if triggered then + observer:onError() + end + end + + local function onCompleted() + if triggered then + observer:onCompleted() + end + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns a new Observable that skips elements until the predicate returns falsy for one of them. +-- @arg {function} predicate - The predicate used to continue skipping values. +-- @returns {Observable} +function Observable:skipWhile(predicate) + predicate = predicate or util.identity + + return Observable.create(function(observer) + local skipping = true + + local function onNext(...) + if skipping then + util.tryWithObserver(observer, function(...) + skipping = predicate(...) + end, ...) + end + + if not skipping then + return observer:onNext(...) + end + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns a new Observable that produces the specified values followed by all elements produced by +-- the source Observable. +-- @arg {*...} values - The values to produce before the Observable begins producing values +-- normally. +-- @returns {Observable} +function Observable:startWith(...) + local values = util.pack(...) + return Observable.create(function(observer) + observer:onNext(util.unpack(values)) + return self:subscribe(observer) + end) +end + +--- Returns an Observable that produces a single value representing the sum of the values produced +-- by the original. +-- @returns {Observable} +function Observable:sum() + return self:reduce(function(x, y) + return x + y + end, 0) +end + +--- Given an Observable that produces Observables, returns an Observable that produces the values +-- produced by the most recently produced Observable. +-- @returns {Observable} +function Observable:switch() + return Observable.create(function(observer) + local innerSubscription + + local function onNext(...) + return observer:onNext(...) + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + local function switch(source) + if innerSubscription then + innerSubscription:unsubscribe() + end + + innerSubscription = source:subscribe(onNext, onError, nil) + end + + local subscription = self:subscribe(switch, onError, onCompleted) + return Subscription.create(function() + if innerSubscription then + innerSubscription:unsubscribe() + end + + if subscription then + subscription:unsubscribe() + end + end) + end) +end + +--- Returns a new Observable that only produces the first n results of the original. +-- @arg {number=1} n - The number of elements to produce before completing. +-- @returns {Observable} +function Observable:take(n) + n = n or 1 + + return Observable.create(function(observer) + local subscription + if n <= 0 then + observer:onCompleted() + return + end + + local i = 1 + + local function onNext(...) + observer:onNext(...) + + i = i + 1 + + if i > n then + if subscription then + subscription:unsubscribe() + end + observer:onCompleted() + end + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + subscription = self:subscribe(onNext, onError, onCompleted) + return subscription + end) +end + +--- Returns an Observable that produces a specified number of elements from the end of a source +-- Observable. +-- @arg {number} count - The number of elements to produce. +-- @returns {Observable} +function Observable:takeLast(count) + if not count or type(count) ~= "number" then + error("Expected a number") + end + + return Observable.create(function(observer) + local buffer = {} + + local function onNext(...) + table.insert(buffer, util.pack(...)) + if #buffer > count then + table.remove(buffer, 1) + end + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + for i = 1, #buffer do + observer:onNext(util.unpack(buffer[i])) + end + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns a new Observable that completes when the specified Observable fires. +-- @arg {Observable} other - The Observable that triggers completion of the original. +-- @returns {Observable} +function Observable:takeUntil(other) + return Observable.create(function(observer) + local subscription + local function onNext(...) + return observer:onNext(...) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + if subscription then + subscription:unsubscribe() + end + return observer:onCompleted() + end + + other:subscribe(onCompleted, onCompleted, onCompleted) + + subscription = self:subscribe(onNext, onError, onCompleted) + return subscription + end) +end + +--- Returns a new Observable that produces elements until the predicate returns falsy. +-- @arg {function} predicate - The predicate used to continue production of values. +-- @returns {Observable} +function Observable:takeWhile(predicate) + predicate = predicate or util.identity + + return Observable.create(function(observer) + local taking = true + local subscription + + local function onNext(...) + if taking then + util.tryWithObserver(observer, function(...) + taking = predicate(...) + end, ...) + + if taking then + return observer:onNext(...) + else + if subscription then + subscription:unsubscribe() + end + return observer:onCompleted() + end + end + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + subscription = self:subscribe(onNext, onError, onCompleted) + return subscription + end) +end + +--- Runs a function each time this Observable has activity. Similar to subscribe but does not +-- create a subscription. +-- @arg {function=} onNext - Run when the Observable produces values. +-- @arg {function=} onError - Run when the Observable encounters a problem. +-- @arg {function=} onCompleted - Run when the Observable completes. +-- @returns {Observable} +function Observable:tap(_onNext, _onError, _onCompleted) + _onNext = _onNext or util.noop + _onError = _onError or util.noop + _onCompleted = _onCompleted or util.noop + + return Observable.create(function(observer) + local function onNext(...) + util.tryWithObserver(observer, function(...) + _onNext(...) + end, ...) + + return observer:onNext(...) + end + + local function onError(message) + util.tryWithObserver(observer, function() + _onError(message) + end) + + return observer:onError(message) + end + + local function onCompleted() + util.tryWithObserver(observer, function() + _onCompleted() + end) + + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns an Observable that unpacks the tables produced by the original. +-- @returns {Observable} +function Observable:unpack() + return self:map(util.unpack) +end + +--- Returns an Observable that takes any values produced by the original that consist of multiple +-- return values and produces each value individually. +-- @returns {Observable} +function Observable:unwrap() + return Observable.create(function(observer) + local function onNext(...) + local values = {...} + for i = 1, #values do + observer:onNext(values[i]) + end + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns an Observable that produces a sliding window of the values produced by the original. +-- @arg {number} size - The size of the window. The returned observable will produce this number +-- of the most recent values as multiple arguments to onNext. +-- @returns {Observable} +function Observable:window(size) + if not size or type(size) ~= "number" then + error("Expected a number") + end + + return Observable.create(function(observer) + local window = {} + + local function onNext(value) + table.insert(window, value) + + if #window >= size then + observer:onNext(util.unpack(window)) + table.remove(window, 1) + end + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end + +--- Returns an Observable that produces values from the original along with the most recently +-- produced value from all other specified Observables. Note that only the first argument from each +-- source Observable is used. +-- @arg {Observable...} sources - The Observables to include the most recent values from. +-- @returns {Observable} +function Observable:with(...) + local sources = {...} + + return Observable.create(function(observer) + local latest = setmetatable({}, { + __len = util.constant(#sources), + }) + local subscriptions = {} + + local function setLatest(i) + return function(value) + latest[i] = value + end + end + + local function onNext(value) + return observer:onNext(value, util.unpack(latest)) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + for i = 1, #sources do + subscriptions[i] = sources[i]:subscribe(setLatest(i), util.noop, util.noop) + end + + subscriptions[#sources + 1] = self:subscribe(onNext, onError, onCompleted) + return Subscription.create(function() + for i = 1, #sources + 1 do + if subscriptions[i] then + subscriptions[i]:unsubscribe() + end + end + end) + end) +end + +--- Returns an Observable that merges the values produced by the source Observables by grouping them +-- by their index. The first onNext event contains the first value of all of the sources, the +-- second onNext event contains the second value of all of the sources, and so on. onNext is called +-- a number of times equal to the number of values produced by the Observable that produces the +-- fewest number of values. +-- @arg {Observable...} sources - The Observables to zip. +-- @returns {Observable} +function Observable.zip(...) + local sources = util.pack(...) + local count = #sources + + return Observable.create(function(observer) + local values = {} + local active = {} + local subscriptions = {} + for i = 1, count do + values[i] = { + n = 0, + } + active[i] = true + end + + local function onNext(i) + return function(value) + table.insert(values[i], value) + values[i].n = values[i].n + 1 + + -- luacheck: ignore i + local ready = true + for i = 1, count do + if values[i].n == 0 then + ready = false + break + end + end + + if ready then + local payload = {} + + for i = 1, count do + payload[i] = table.remove(values[i], 1) + values[i].n = values[i].n - 1 + end + + observer:onNext(util.unpack(payload)) + end + end + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted(i) + return function() + active[i] = nil + if not next(active) or values[i].n == 0 then + return observer:onCompleted() + end + end + end + + for i = 1, count do + subscriptions[i] = sources[i]:subscribe(onNext(i), onError, onCompleted(i)) + end + + return Subscription.create(function() + for i = 1, count do + if subscriptions[i] then + subscriptions[i]:unsubscribe() + end + end + end) + end) +end + +--- @class ImmediateScheduler +-- @description Schedules Observables by running all operations immediately. +local ImmediateScheduler = {} +ImmediateScheduler.__index = ImmediateScheduler +ImmediateScheduler.__tostring = util.constant("ImmediateScheduler") + +--- Creates a new ImmediateScheduler. +-- @returns {ImmediateScheduler} +function ImmediateScheduler.create() + return setmetatable({}, ImmediateScheduler) +end + +--- Schedules a function to be run on the scheduler. It is executed immediately. +-- @arg {function} action - The function to execute. +function ImmediateScheduler:schedule(action) + local _ = self + action() +end + +--- @class CooperativeScheduler +-- @description Manages Observables using coroutines and a virtual clock that must be updated +-- manually. +local CooperativeScheduler = {} +CooperativeScheduler.__index = CooperativeScheduler +CooperativeScheduler.__tostring = util.constant("CooperativeScheduler") + +--- Creates a new CooperativeScheduler. +-- @arg {number=0} currentTime - A time to start the scheduler at. +-- @returns {CooperativeScheduler} +function CooperativeScheduler.create(currentTime) + local self = { + tasks = {}, + currentTime = currentTime or 0, + } + + return setmetatable(self, CooperativeScheduler) +end + +--- Schedules a function to be run after an optional delay. Returns a subscription that will stop +-- the action from running. +-- @arg {function} action - The function to execute. Will be converted into a coroutine. The +-- coroutine may yield execution back to the scheduler with an optional +-- number, which will put it to sleep for a time period. +-- @arg {number=0} delay - Delay execution of the action by a virtual time period. +-- @returns {Subscription} +function CooperativeScheduler:schedule(action, delay) + local task = { + thread = coroutine.create(action), + due = self.currentTime + (delay or 0), + } + + table.insert(self.tasks, task) + + return Subscription.create(function() + return self:unschedule(task) + end) +end + +function CooperativeScheduler:unschedule(task) + for i = 1, #self.tasks do + if self.tasks[i] == task then + table.remove(self.tasks, i) + end + end +end + +--- Triggers an update of the CooperativeScheduler. The clock will be advanced and the scheduler +-- will run any coroutines that are due to be run. +-- @arg {number=0} delta - An amount of time to advance the clock by. It is common to pass in the +-- time in seconds or milliseconds elapsed since this function was last +-- called. +function CooperativeScheduler:update(delta) + self.currentTime = self.currentTime + (delta or 0) + + local i = 1 + while i <= #self.tasks do + local task = self.tasks[i] + + if self.currentTime >= task.due then + local success, delay = coroutine.resume(task.thread) + + if coroutine.status(task.thread) == "dead" then + table.remove(self.tasks, i) + else + task.due = math.max(task.due + (delay or 0), self.currentTime) + i = i + 1 + end + + if not success then + error(delay) + end + else + i = i + 1 + end + end +end + +--- Returns whether or not the CooperativeScheduler's queue is empty. +function CooperativeScheduler:isEmpty() + return not next(self.tasks) +end + +--- @class TimeoutScheduler +-- @description A scheduler that uses luvit's timer library to schedule events on an event loop. +local TimeoutScheduler = {} +TimeoutScheduler.__index = TimeoutScheduler +TimeoutScheduler.__tostring = util.constant("TimeoutScheduler") + +--- Creates a new TimeoutScheduler. +-- @returns {TimeoutScheduler} +function TimeoutScheduler.create() + return setmetatable({}, TimeoutScheduler) +end + +--- Schedules an action to run at a future point in time. +-- @arg {function} action - The action to run. +-- @arg {number=0} delay - The delay, in milliseconds. +-- @returns {Subscription} +function TimeoutScheduler:schedule(action, delay, ...) + local _ = self + local timer = require "rx.timer" + local handle = timer.setTimeout(delay, action, ...) + return Subscription.create(function() + timer.clearTimeout(handle) + end) +end + +--- @class Subject +-- @description Subjects function both as an Observer and as an Observable. Subjects inherit all +-- Observable functions, including subscribe. Values can also be pushed to the Subject, which will +-- be broadcasted to any subscribed Observers. +local Subject = setmetatable({}, Observable) +Subject.__index = Subject +Subject.__tostring = util.constant("Subject") + +--- Creates a new Subject. +-- @returns {Subject} +function Subject.create() + local self = { + observers = {}, + stopped = false, + } + + return setmetatable(self, Subject) +end + +--- Creates a new Observer and attaches it to the Subject. +-- @arg {function|table} onNext|observer - A function called when the Subject produces a value or +-- an existing Observer to attach to the Subject. +-- @arg {function} onError - Called when the Subject terminates due to an error. +-- @arg {function} onCompleted - Called when the Subject completes normally. +function Subject:subscribe(onNext, onError, onCompleted) + local observer + + if util.isa(onNext, Observer) then + observer = onNext + else + observer = Observer.create(onNext, onError, onCompleted) + end + + table.insert(self.observers, observer) + + return Subscription.create(function() + for i = 1, #self.observers do + if self.observers[i] == observer then + table.remove(self.observers, i) + return + end + end + end) +end + +--- Pushes zero or more values to the Subject. They will be broadcasted to all Observers. +-- @arg {*...} values +function Subject:onNext(...) + if not self.stopped then + for i = #self.observers, 1, -1 do + self.observers[i]:onNext(...) + end + end +end + +--- Signal to all Observers that an error has occurred. +-- @arg {string=} message - A string describing what went wrong. +function Subject:onError(message) + if not self.stopped then + for i = #self.observers, 1, -1 do + self.observers[i]:onError(message) + end + + self.stopped = true + end +end + +--- Signal to all Observers that the Subject will not produce any more values. +function Subject:onCompleted() + if not self.stopped then + for i = #self.observers, 1, -1 do + self.observers[i]:onCompleted() + end + + self.stopped = true + end +end + +Subject.__call = Subject.onNext + +--- @class AnonymousSubject +-- @description Like Subject, AnonymousSubjects function both as an Observer and as an Observable. +-- The functionality of both sides of an AnonymousSubject is provided by an external observer +-- and observable. Usually, the entity that creates the AnonymousSubject will subscribe to +-- the observable so that pushing a value to the AnonymousSubject results in an effect on the +-- observer. + +local AnonymousSubject = setmetatable({}, Observable) +AnonymousSubject.__index = AnonymousSubject +AnonymousSubject.__tostring = util.constant("AnonymousSubject") + +--- Creates a new AnonymousSubject +-- @arg{Observer} destination - the observer (input) side of the AnonymousSubject +-- @arg{Observable} source - the observable (output) side of the AnonymousSubject +-- @returns {AnonymousSubject} +function AnonymousSubject.create(_destination, _source) + local self = { + destination = _destination, + source = _source, + } + + return setmetatable(self, AnonymousSubject) +end + +--- Attaches an Observer to this AnonymousSubject's observable. +-- @arg {Observer|function} onNext - An Observer, or a function which is called when the Observable +-- produces a value. If a function, then the three-argument semantics are used, which are a +-- shorthand for creating an Observer and passing it to this same method. +-- @arg {function} onError - Called when the Observable terminates due to an error. +-- @arg {function} onCompleted - Called when the Observable completes normally. +function AnonymousSubject:subscribe(onNext, onError, onCompleted) + if self.source then + return self.source:subscribe(onNext, onError, onCompleted) + else + return Subscription.create(util.noop) + end +end + +--- Pushes zero or more values to the AnonymousSubject. +-- @arg {*...} values +function AnonymousSubject:onNext(...) + if self.destination then + self.destination:onNext(...) + end +end + +--- Signal to the AnonymousSubject that an error has occurred. +-- @arg {string=} message - A string describing what went wrong. +function AnonymousSubject:onError(message) + if self.destination then + self.destination:onError(message) + end +end + +--- Signal to the AnonymousSubject that its observer will not be fed any more values. +function AnonymousSubject:onCompleted() + if self.destination then + self.destination:onCompleted() + end +end + +--- @class AsyncSubject +-- @description AsyncSubjects are subjects that produce either no values or a single value. If +-- multiple values are produced via onNext, only the last one is used. If onError is called, then +-- no value is produced and onError is called on any subscribed Observers. If an Observer +-- subscribes and the AsyncSubject has already terminated, the Observer will immediately receive the +-- value or the error. +local AsyncSubject = setmetatable({}, Observable) +AsyncSubject.__index = AsyncSubject +AsyncSubject.__tostring = util.constant("AsyncSubject") + +--- Creates a new AsyncSubject. +-- @returns {AsyncSubject} +function AsyncSubject.create() + local self = { + observers = {}, + stopped = false, + value = nil, + errorMessage = nil, + } + + return setmetatable(self, AsyncSubject) +end + +--- Creates a new Observer and attaches it to the AsyncSubject. +-- @arg {function|table} onNext|observer - A function called when the AsyncSubject produces a value +-- or an existing Observer to attach to the AsyncSubject. +-- @arg {function} onError - Called when the AsyncSubject terminates due to an error. +-- @arg {function} onCompleted - Called when the AsyncSubject completes normally. +function AsyncSubject:subscribe(onNext, onError, onCompleted) + local observer + + if util.isa(onNext, Observer) then + observer = onNext + else + observer = Observer.create(onNext, onError, onCompleted) + end + + if self.value then + observer:onNext(util.unpack(self.value)) + observer:onCompleted() + return + elseif self.errorMessage then + observer:onError(self.errorMessage) + return + end + + table.insert(self.observers, observer) + + return Subscription.create(function() + for i = 1, #self.observers do + if self.observers[i] == observer then + table.remove(self.observers, i) + return + end + end + end) +end + +--- Pushes zero or more values to the AsyncSubject. +-- @arg {*...} values +function AsyncSubject:onNext(...) + if not self.stopped then + self.value = util.pack(...) + end +end + +--- Signal to all Observers that an error has occurred. +-- @arg {string=} message - A string describing what went wrong. +function AsyncSubject:onError(message) + if not self.stopped then + self.errorMessage = message + + for i = 1, #self.observers do + self.observers[i]:onError(self.errorMessage) + end + + self.stopped = true + end +end + +--- Signal to all Observers that the AsyncSubject will not produce any more values. +function AsyncSubject:onCompleted() + if not self.stopped then + for i = 1, #self.observers do + if self.value then + self.observers[i]:onNext(util.unpack(self.value)) + end + + self.observers[i]:onCompleted() + end + + self.stopped = true + end +end + +AsyncSubject.__call = AsyncSubject.onNext + +--- @class BehaviorSubject +-- @description A Subject that tracks its current value. Provides an accessor to retrieve the most +-- recent pushed value, and all subscribers immediately receive the latest value. +local BehaviorSubject = setmetatable({}, Subject) +BehaviorSubject.__index = BehaviorSubject +BehaviorSubject.__tostring = util.constant("BehaviorSubject") + +--- Creates a new BehaviorSubject. +-- @arg {*...} value - The initial values. +-- @returns {BehaviorSubject} +function BehaviorSubject.create(...) + local self = { + observers = {}, + stopped = false, + } + + if select("#", ...) > 0 then + self.value = util.pack(...) + end + + return setmetatable(self, BehaviorSubject) +end + +--- Creates a new Observer and attaches it to the BehaviorSubject. Immediately broadcasts the most +-- recent value to the Observer. +-- @arg {function} onNext - Called when the BehaviorSubject produces a value. +-- @arg {function} onError - Called when the BehaviorSubject terminates due to an error. +-- @arg {function} onCompleted - Called when the BehaviorSubject completes normally. +function BehaviorSubject:subscribe(onNext, onError, onCompleted) + local observer + + if util.isa(onNext, Observer) then + observer = onNext + else + observer = Observer.create(onNext, onError, onCompleted) + end + + local subscription = Subject.subscribe(self, observer) + + if self.value then + observer:onNext(util.unpack(self.value)) + end + + return subscription +end + +--- Pushes zero or more values to the BehaviorSubject. They will be broadcasted to all Observers. +-- @arg {*...} values +function BehaviorSubject:onNext(...) + self.value = util.pack(...) + return Subject.onNext(self, ...) +end + +--- Returns the last value emitted by the BehaviorSubject, or the initial value passed to the +-- constructor if nothing has been emitted yet. +-- @returns {*...} +function BehaviorSubject:getValue() + if self.value ~= nil then + return util.unpack(self.value) + end +end + +BehaviorSubject.__call = BehaviorSubject.onNext + +--- @class ReplaySubject +-- @description A Subject that provides new Subscribers with some or all of the most recently +-- produced values upon subscription. +local ReplaySubject = setmetatable({}, Subject) +ReplaySubject.__index = ReplaySubject +ReplaySubject.__tostring = util.constant("ReplaySubject") + +--- Creates a new ReplaySubject. +-- @arg {number=} bufferSize - The number of values to send to new subscribers. If nil, an infinite +-- buffer is used (note that this could lead to memory issues). +-- @returns {ReplaySubject} +function ReplaySubject.create(n) + local self = { + observers = {}, + stopped = false, + buffer = {}, + bufferSize = n, + } + + return setmetatable(self, ReplaySubject) +end + +--- Creates a new Observer and attaches it to the ReplaySubject. Immediately broadcasts the most +-- contents of the buffer to the Observer. +-- @arg {function} onNext - Called when the ReplaySubject produces a value. +-- @arg {function} onError - Called when the ReplaySubject terminates due to an error. +-- @arg {function} onCompleted - Called when the ReplaySubject completes normally. +function ReplaySubject:subscribe(onNext, onError, onCompleted) + local observer + + if util.isa(onNext, Observer) then + observer = onNext + else + observer = Observer.create(onNext, onError, onCompleted) + end + + local subscription = Subject.subscribe(self, observer) + + for i = 1, #self.buffer do + observer:onNext(util.unpack(self.buffer[i])) + end + + return subscription +end + +--- Pushes zero or more values to the ReplaySubject. They will be broadcasted to all Observers. +-- @arg {*...} values +function ReplaySubject:onNext(...) + table.insert(self.buffer, util.pack(...)) + if self.bufferSize and #self.buffer > self.bufferSize then + table.remove(self.buffer, 1) + end + + return Subject.onNext(self, ...) +end + +ReplaySubject.__call = ReplaySubject.onNext + +Observable.wrap = Observable.buffer +Observable["repeat"] = Observable.replicate + +return { + util = util, + Subscription = Subscription, + Observer = Observer, + Observable = Observable, + ImmediateScheduler = ImmediateScheduler, + CooperativeScheduler = CooperativeScheduler, + TimeoutScheduler = TimeoutScheduler, + Subject = Subject, + AnonymousSubject = AnonymousSubject, + AsyncSubject = AsyncSubject, + BehaviorSubject = BehaviorSubject, + ReplaySubject = ReplaySubject, +} diff --git a/framework/lualib/thirdparty/rx/observable.lua b/framework/lualib/thirdparty/rx/observable.lua new file mode 100755 index 0000000..5013dd0 --- /dev/null +++ b/framework/lualib/thirdparty/rx/observable.lua @@ -0,0 +1,214 @@ +local Observer = require "rx.observer" +local util = require "rx.util" + +--- @class Observable +-- @description Observables push values to Observers. +local Observable = {} +Observable.__index = Observable +Observable.__tostring = util.constant("Observable") + +--- Creates a new Observable. +-- @arg {function} subscribe - The subscription function that produces values. +-- @returns {Observable} +function Observable.create(subscribe) + local self = { + _subscribe = subscribe, + } + + return setmetatable(self, Observable) +end + +--- Shorthand for creating an Observer and passing it to this Observable's subscription function. +-- @arg {function} onNext - Called when the Observable produces a value. +-- @arg {function} onError - Called when the Observable terminates due to an error. +-- @arg {function} onCompleted - Called when the Observable completes normally. +function Observable:subscribe(onNext, onError, onCompleted) + if type(onNext) == "table" then + return self._subscribe(onNext) + else + return self._subscribe(Observer.create(onNext, onError, onCompleted)) + end +end + +--- Returns an Observable that immediately completes without producing a value. +function Observable.empty() + return Observable.create(function(observer) + observer:onCompleted() + end) +end + +--- Returns an Observable that never produces values and never completes. +function Observable.never() + return Observable.create(function() + end) +end + +--- Returns an Observable that immediately produces an error. +function Observable.throw(message) + return Observable.create(function(observer) + observer:onError(message) + end) +end + +--- Creates an Observable that produces a set of values. +-- @arg {*...} values +-- @returns {Observable} +function Observable.of(...) + local args = {...} + local argCount = select("#", ...) + return Observable.create(function(observer) + for i = 1, argCount do + observer:onNext(args[i]) + end + + observer:onCompleted() + end) +end + +--- Creates an Observable that produces a range of values in a manner similar to a Lua for loop. +-- @arg {number} initial - The first value of the range, or the upper limit if no other arguments +-- are specified. +-- @arg {number=} limit - The second value of the range. +-- @arg {number=1} step - An amount to increment the value by each iteration. +-- @returns {Observable} +function Observable.fromRange(initial, limit, step) + if not limit and not step then + initial, limit = 1, initial + end + + step = step or 1 + + return Observable.create(function(observer) + for i = initial, limit, step do + observer:onNext(i) + end + + observer:onCompleted() + end) +end + +--- Creates an Observable that produces values from a table. +-- @arg {table} table - The table used to create the Observable. +-- @arg {function=pairs} iterator - An iterator used to iterate the table, e.g. pairs or ipairs. +-- @arg {boolean} keys - Whether or not to also emit the keys of the table. +-- @returns {Observable} +function Observable.fromTable(t, iterator, keys) + iterator = iterator or pairs + return Observable.create(function(observer) + for key, value in iterator(t) do + observer:onNext(value, keys and key or nil) + end + + observer:onCompleted() + end) +end + +--- Creates an Observable that produces values when the specified coroutine yields. +-- @arg {thread|function} fn - A coroutine or function to use to generate values. Note that if a +-- coroutine is used, the values it yields will be shared by all +-- subscribed Observers (influenced by the Scheduler), whereas a new +-- coroutine will be created for each Observer when a function is used. +-- @returns {Observable} +function Observable.fromCoroutine(fn, scheduler) + return Observable.create(function(observer) + local thread = type(fn) == "function" and coroutine.create(fn) or fn + return scheduler:schedule(function() + while not observer.stopped do + local success, value = coroutine.resume(thread) + + if success then + observer:onNext(value) + else + return observer:onError(value) + end + + if coroutine.status(thread) == "dead" then + return observer:onCompleted() + end + + coroutine.yield() + end + end) + end) +end + +--- Creates an Observable that produces values from a file, line by line. +-- @arg {string} filename - The name of the file used to create the Observable +-- @returns {Observable} +function Observable.fromFileByLine(filename) + return Observable.create(function(observer) + local file = io.open(filename, "r") + if file then + file:close() + + for line in io.lines(filename) do + observer:onNext(line) + end + + return observer:onCompleted() + else + return observer:onError(filename) + end + end) +end + +--- Creates an Observable that creates a new Observable for each observer using a factory function. +-- @arg {function} factory - A function that returns an Observable. +-- @returns {Observable} +function Observable.defer(fn) + if not fn or type(fn) ~= "function" then + error("Expected a function") + end + + return setmetatable({ + subscribe = function(_, ...) + local observable = fn() + return observable:subscribe(...) + end, + }, Observable) +end + +--- Returns an Observable that repeats a value a specified number of times. +-- @arg {*} value - The value to repeat. +-- @arg {number=} count - The number of times to repeat the value. If left unspecified, the value +-- is repeated an infinite number of times. +-- @returns {Observable} +function Observable.replicate(value, count) + return Observable.create(function(observer) + while count == nil or count > 0 do + observer:onNext(value) + if count then + count = count - 1 + end + end + observer:onCompleted() + end) +end + +--- Subscribes to this Observable and prints values it produces. +-- @arg {string=} name - Prefixes the printed messages with a name. +-- @arg {function=tostring} formatter - A function that formats one or more values to be printed. +function Observable:dump(name, formatter) + name = name and (name .. " ") or "" + + local onNext + if formatter then + onNext = function(...) + print(name .. "onNext: " .. formatter(...)) + end + else + onNext = function(...) + print(name .. "onNext: ", ...) + end + end + local onError = function(e) + print(name .. "onError: " .. e) + end + local onCompleted = function() + print(name .. "onCompleted") + end + + return self:subscribe(onNext, onError, onCompleted) +end + +return Observable diff --git a/framework/lualib/thirdparty/rx/observer.lua b/framework/lualib/thirdparty/rx/observer.lua new file mode 100755 index 0000000..ebd62dd --- /dev/null +++ b/framework/lualib/thirdparty/rx/observer.lua @@ -0,0 +1,50 @@ +local util = require "rx.util" + +--- @class Observer +-- @description Observers are simple objects that receive values from Observables. +local Observer = {} +Observer.__index = Observer +Observer.__tostring = util.constant("Observer") + +--- Creates a new Observer. +-- @arg {function=} onNext - Called when the Observable produces a value. +-- @arg {function=} onError - Called when the Observable terminates due to an error. +-- @arg {function=} onCompleted - Called when the Observable completes normally. +-- @returns {Observer} +function Observer.create(onNext, onError, onCompleted) + local self = { + _onNext = onNext or util.noop, + _onError = onError or error, + _onCompleted = onCompleted or util.noop, + stopped = false, + } + + return setmetatable(self, Observer) +end + +--- Pushes zero or more values to the Observer. +-- @arg {*...} values +function Observer:onNext(...) + if not self.stopped then + self._onNext(...) + end +end + +--- Notify the Observer that an error has occurred. +-- @arg {string=} message - A string describing what went wrong. +function Observer:onError(message) + if not self.stopped then + self.stopped = true + self._onError(message) + end +end + +--- Notify the Observer that the sequence has completed and will produce no more values. +function Observer:onCompleted() + if not self.stopped then + self.stopped = true + self._onCompleted() + end +end + +return Observer diff --git a/framework/lualib/thirdparty/rx/operators/all.lua b/framework/lualib/thirdparty/rx/operators/all.lua new file mode 100755 index 0000000..bd1f5c4 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/all.lua @@ -0,0 +1,35 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Determine whether all items emitted by an Observable meet some criteria. +-- @arg {function=identity} predicate - The predicate used to evaluate objects. +function Observable:all(predicate) + predicate = predicate or util.identity + + return Observable.create(function(observer) + local subscription + local function onNext(...) + util.tryWithObserver(observer, function(...) + if not predicate(...) then + observer:onNext(false) + if subscription then + subscription:unsubscribe() + end + observer:onCompleted() + end + end, ...) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + observer:onNext(true) + return observer:onCompleted() + end + + subscription = self:subscribe(onNext, onError, onCompleted) + return subscription + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/amb.lua b/framework/lualib/thirdparty/rx/operators/amb.lua new file mode 100755 index 0000000..21af1ff --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/amb.lua @@ -0,0 +1,65 @@ +local Observable = require "rx.observable" +local Subscription = require "rx.subscription" + +--- Given a set of Observables, produces values from only the first one to produce a value. +-- @arg {Observable...} observables +-- @returns {Observable} +function Observable.amb(a, b, ...) + if not a or not b then + return a + end + + return Observable.create(function(observer) + local subscriptionA, subscriptionB + + local function onNextA(...) + if subscriptionB then + subscriptionB:unsubscribe() + end + observer:onNext(...) + end + + local function onErrorA(e) + if subscriptionB then + subscriptionB:unsubscribe() + end + observer:onError(e) + end + + local function onCompletedA() + if subscriptionB then + subscriptionB:unsubscribe() + end + observer:onCompleted() + end + + local function onNextB(...) + if subscriptionA then + subscriptionA:unsubscribe() + end + observer:onNext(...) + end + + local function onErrorB(e) + if subscriptionA then + subscriptionA:unsubscribe() + end + observer:onError(e) + end + + local function onCompletedB() + if subscriptionA then + subscriptionA:unsubscribe() + end + observer:onCompleted() + end + + subscriptionA = a:subscribe(onNextA, onErrorA, onCompletedA) + subscriptionB = b:subscribe(onNextB, onErrorB, onCompletedB) + + return Subscription.create(function() + subscriptionA:unsubscribe() + subscriptionB:unsubscribe() + end) + end):amb(...) +end diff --git a/framework/lualib/thirdparty/rx/operators/average.lua b/framework/lualib/thirdparty/rx/operators/average.lua new file mode 100755 index 0000000..92ecb0e --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/average.lua @@ -0,0 +1,28 @@ +local Observable = require "rx.observable" + +--- Returns an Observable that produces the average of all values produced by the original. +-- @returns {Observable} +function Observable:average() + return Observable.create(function(observer) + local sum, count = 0, 0 + + local function onNext(value) + sum = sum + value + count = count + 1 + end + + local function onError(e) + observer:onError(e) + end + + local function onCompleted() + if count > 0 then + observer:onNext(sum / count) + end + + observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/buffer.lua b/framework/lualib/thirdparty/rx/operators/buffer.lua new file mode 100755 index 0000000..c6a6234 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/buffer.lua @@ -0,0 +1,44 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns an Observable that buffers values from the original and produces them as multiple +-- values. +-- @arg {number} size - The size of the buffer. +function Observable:buffer(size) + if not size or type(size) ~= "number" then + error("Expected a number") + end + + return Observable.create(function(observer) + local buffer = {} + + local function emit() + if #buffer > 0 then + observer:onNext(util.unpack(buffer)) + buffer = {} + end + end + + local function onNext(...) + local values = {...} + for i = 1, #values do + table.insert(buffer, values[i]) + if #buffer >= size then + emit() + end + end + end + + local function onError(message) + emit() + return observer:onError(message) + end + + local function onCompleted() + emit() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/catch.lua b/framework/lualib/thirdparty/rx/operators/catch.lua new file mode 100755 index 0000000..4d05859 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/catch.lua @@ -0,0 +1,45 @@ +local Observable = require 'rx.observable' +local util = require 'rx.util' + +--- Returns an Observable that intercepts any errors from the previous and replace them with values +-- produced by a new Observable. +-- @arg {function|Observable} handler - An Observable or a function that returns an Observable to +-- replace the source Observable in the event of an error. +-- @returns {Observable} +function Observable:catch(handler) + handler = handler and (type(handler) == 'function' and handler or util.constant(handler)) + + return Observable.create(function(observer) + local subscription + + local function onNext(...) + return observer:onNext(...) + end + + local function onError(e) + if not handler then + if subscription then + subscription:unsubscribe() + end + return observer:onCompleted() + end + + local success, _continue = pcall(handler, e) + if success and _continue then + if subscription then + subscription:unsubscribe() + end + _continue:subscribe(observer) + else + observer:onError(success and e or _continue) + end + end + + local function onCompleted() + observer:onCompleted() + end + + subscription = self:subscribe(onNext, onError, onCompleted) + return subscription + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/combineLatest.lua b/framework/lualib/thirdparty/rx/operators/combineLatest.lua new file mode 100755 index 0000000..46c6332 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/combineLatest.lua @@ -0,0 +1,68 @@ +local Observable = require "rx.observable" +local Subscription = require "rx.subscription" +local util = require "rx.util" + +--- Returns a new Observable that runs a combinator function on the most recent values from a set +-- of Observables whenever any of them produce a new value. The results of the combinator function +-- are produced by the new Observable. +-- @arg {Observable...} observables - One or more Observables to combine. +-- @arg {function} combinator - A function that combines the latest result from each Observable and +-- returns a single value. +-- @returns {Observable} +function Observable:combineLatest(...) + local sources = {...} + local combinator = table.remove(sources) + if type(combinator) ~= "function" then + table.insert(sources, combinator) + combinator = function(...) + return ... + end + end + table.insert(sources, 1, self) + + return Observable.create(function(observer) + local latest = {} + local pending = {util.unpack(sources)} + local completed = {} + local subscription = {} + + local function onNext(i) + return function(value) + latest[i] = value + pending[i] = nil + + if not next(pending) then + util.tryWithObserver(observer, function() + observer:onNext(combinator(util.unpack(latest))) + end) + end + end + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted(i) + return function() + table.insert(completed, i) + + if #completed == #sources then + observer:onCompleted() + end + end + end + + for i = 1, #sources do + subscription[i] = sources[i]:subscribe(onNext(i), onError, onCompleted(i)) + end + + return Subscription.create(function() + for i = 1, #sources do + if subscription[i] then + subscription[i]:unsubscribe() + end + end + end) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/compact.lua b/framework/lualib/thirdparty/rx/operators/compact.lua new file mode 100755 index 0000000..eea8be0 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/compact.lua @@ -0,0 +1,8 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns a new Observable that produces the values of the first with falsy values removed. +-- @returns {Observable} +function Observable:compact() + return self:filter(util.identity) +end diff --git a/framework/lualib/thirdparty/rx/operators/concat.lua b/framework/lualib/thirdparty/rx/operators/concat.lua new file mode 100755 index 0000000..94209ad --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/concat.lua @@ -0,0 +1,34 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns a new Observable that produces the values produced by all the specified Observables in +-- the order they are specified. +-- @arg {Observable...} sources - The Observables to concatenate. +-- @returns {Observable} +function Observable:concat(other, ...) + if not other then + return self + end + + local others = {...} + + return Observable.create(function(observer) + local function onNext(...) + return observer:onNext(...) + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + local function chain() + return other:concat(util.unpack(others)):subscribe(onNext, onError, onCompleted) + end + + return self:subscribe(onNext, onError, chain) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/contains.lua b/framework/lualib/thirdparty/rx/operators/contains.lua new file mode 100755 index 0000000..e76d175 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/contains.lua @@ -0,0 +1,46 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns a new Observable that produces a single boolean value representing whether or not the +-- specified value was produced by the original. +-- @arg {*} value - The value to search for. == is used for equality testing. +-- @returns {Observable} +function Observable:contains(value) + return Observable.create(function(observer) + local subscription + + local function onNext(...) + local args = util.pack(...) + + if #args == 0 and value == nil then + observer:onNext(true) + if subscription then + subscription:unsubscribe() + end + return observer:onCompleted() + end + + for i = 1, #args do + if args[i] == value then + observer:onNext(true) + if subscription then + subscription:unsubscribe() + end + return observer:onCompleted() + end + end + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + observer:onNext(false) + return observer:onCompleted() + end + + subscription = self:subscribe(onNext, onError, onCompleted) + return subscription + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/count.lua b/framework/lualib/thirdparty/rx/operators/count.lua new file mode 100755 index 0000000..1e9a752 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/count.lua @@ -0,0 +1,32 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns an Observable that produces a single value representing the number of values produced +-- by the source value that satisfy an optional predicate. +-- @arg {function=} predicate - The predicate used to match values. +function Observable:count(predicate) + predicate = predicate or util.constant(true) + + return Observable.create(function(observer) + local count = 0 + + local function onNext(...) + util.tryWithObserver(observer, function(...) + if predicate(...) then + count = count + 1 + end + end, ...) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + observer:onNext(count) + observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/debounce.lua b/framework/lualib/thirdparty/rx/operators/debounce.lua new file mode 100755 index 0000000..0d4584b --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/debounce.lua @@ -0,0 +1,42 @@ +local Observable = require "rx.observable" +local Subscription = require "rx.subscription" +local util = require "rx.util" + +--- Returns a new throttled Observable that waits to produce values until a timeout has expired, at +-- which point it produces the latest value from the source Observable. Whenever the source +-- Observable produces a value, the timeout is reset. +-- @arg {number|function} time - An amount in milliseconds to wait before producing the last value. +-- @arg {Scheduler} scheduler - The scheduler to run the Observable on. +-- @returns {Observable} +function Observable:debounce(time, scheduler) + time = time or 0 + + return Observable.create(function(observer) + local debounced = {} + + local function wrap(key) + return function(...) + if debounced[key] then + debounced[key]:unsubscribe() + end + + local values = util.pack(...) + + debounced[key] = scheduler:schedule(function() + return observer[key](observer, util.unpack(values)) + end, time) + end + end + + local subscription = self:subscribe(wrap("onNext"), wrap("onError"), wrap("onCompleted")) + + return Subscription.create(function() + if subscription then + subscription:unsubscribe() + end + for _, timeout in pairs(debounced) do + timeout:unsubscribe() + end + end) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/defaultIfEmpty.lua b/framework/lualib/thirdparty/rx/operators/defaultIfEmpty.lua new file mode 100755 index 0000000..889c3ff --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/defaultIfEmpty.lua @@ -0,0 +1,34 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns a new Observable that produces a default set of items if the source Observable produces +-- no values. +-- @arg {*...} values - Zero or more values to produce if the source completes without emitting +-- anything. +-- @returns {Observable} +function Observable:defaultIfEmpty(...) + local defaults = util.pack(...) + + return Observable.create(function(observer) + local hasValue = false + + local function onNext(...) + hasValue = true + observer:onNext(...) + end + + local function onError(e) + observer:onError(e) + end + + local function onCompleted() + if not hasValue then + observer:onNext(util.unpack(defaults)) + end + + observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/delay.lua b/framework/lualib/thirdparty/rx/operators/delay.lua new file mode 100755 index 0000000..3cb62f8 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/delay.lua @@ -0,0 +1,37 @@ +local Observable = require "rx.observable" +local Subscription = require "rx.subscription" +local util = require "rx.util" + +--- Returns a new Observable that produces the values of the original delayed by a time period. +-- @arg {number|function} time - An amount in milliseconds to delay by, or a function which returns +-- this value. +-- @arg {Scheduler} scheduler - The scheduler to run the Observable on. +-- @returns {Observable} +function Observable:delay(time, scheduler) + time = type(time) ~= "function" and util.constant(time) or time + + return Observable.create(function(observer) + local actions = {} + + local function delay(key) + return function(...) + local arg = util.pack(...) + local handle = scheduler:schedule(function() + observer[key](observer, util.unpack(arg)) + end, time()) + table.insert(actions, handle) + end + end + + local subscription = self:subscribe(delay("onNext"), delay("onError"), delay("onCompleted")) + + return Subscription.create(function() + if subscription then + subscription:unsubscribe() + end + for i = 1, #actions do + actions[i]:unsubscribe() + end + end) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/distinct.lua b/framework/lualib/thirdparty/rx/operators/distinct.lua new file mode 100755 index 0000000..ea063b3 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/distinct.lua @@ -0,0 +1,27 @@ +local Observable = require 'rx.observable' + +--- Returns a new Observable that produces the values from the original with duplicates removed. +-- @returns {Observable} +function Observable:distinct() + return Observable.create(function(observer) + local values = {} + + local function onNext(x) + if not values[x] then + observer:onNext(x) + end + + values[x] = true + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/distinctUntilChanged.lua b/framework/lualib/thirdparty/rx/operators/distinctUntilChanged.lua new file mode 100755 index 0000000..6a998e7 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/distinctUntilChanged.lua @@ -0,0 +1,36 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns an Observable that only produces values from the original if they are different from +-- the previous value. +-- @arg {function} comparator - A function used to compare 2 values. If unspecified, == is used. +-- @returns {Observable} +function Observable:distinctUntilChanged(comparator) + comparator = comparator or util.eq + + return Observable.create(function(observer) + local first = true + local currentValue = nil + + local function onNext(value, ...) + local values = util.pack(...) + util.tryWithObserver(observer, function() + if first or not comparator(value, currentValue) then + observer:onNext(value, util.unpack(values)) + currentValue = value + first = false + end + end) + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/elementAt.lua b/framework/lualib/thirdparty/rx/operators/elementAt.lua new file mode 100755 index 0000000..1962fce --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/elementAt.lua @@ -0,0 +1,38 @@ +local Observable = require "rx.observable" + +--- Returns an Observable that produces the nth element produced by the source Observable. +-- @arg {number} index - The index of the item, with an index of 1 representing the first. +-- @returns {Observable} +function Observable:elementAt(index) + if not index or type(index) ~= "number" then + error("Expected a number") + end + + return Observable.create(function(observer) + local subscription + local i = 1 + + local function onNext(...) + if i == index then + observer:onNext(...) + observer:onCompleted() + if subscription then + subscription:unsubscribe() + end + else + i = i + 1 + end + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + subscription = self:subscribe(onNext, onError, onCompleted) + return subscription + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/filter.lua b/framework/lualib/thirdparty/rx/operators/filter.lua new file mode 100755 index 0000000..6caaf82 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/filter.lua @@ -0,0 +1,29 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns a new Observable that only produces values of the first that satisfy a predicate. +-- @arg {function} predicate - The predicate used to filter values. +-- @returns {Observable} +function Observable:filter(predicate) + predicate = predicate or util.identity + + return Observable.create(function(observer) + local function onNext(...) + util.tryWithObserver(observer, function(...) + if predicate(...) then + return observer:onNext(...) + end + end, ...) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/find.lua b/framework/lualib/thirdparty/rx/operators/find.lua new file mode 100755 index 0000000..4ef1097 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/find.lua @@ -0,0 +1,36 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns a new Observable that produces the first value of the original that satisfies a +-- predicate. +-- @arg {function} predicate - The predicate used to find a value. +function Observable:find(predicate) + predicate = predicate or util.identity + + return Observable.create(function(observer) + local subscription + + local function onNext(...) + util.tryWithObserver(observer, function(...) + if predicate(...) then + observer:onNext(...) + if subscription then + subscription:unsubscribe() + end + return observer:onCompleted() + end + end, ...) + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + subscription = self:subscribe(onNext, onError, onCompleted) + return subscription + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/first.lua b/framework/lualib/thirdparty/rx/operators/first.lua new file mode 100755 index 0000000..2c4bdf1 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/first.lua @@ -0,0 +1,7 @@ +local Observable = require "rx.observable" + +--- Returns a new Observable that only produces the first result of the original. +-- @returns {Observable} +function Observable:first() + return self:take(1) +end diff --git a/framework/lualib/thirdparty/rx/operators/flatMap.lua b/framework/lualib/thirdparty/rx/operators/flatMap.lua new file mode 100755 index 0000000..03f2770 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/flatMap.lua @@ -0,0 +1,11 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns a new Observable that transform the items emitted by an Observable into Observables, +-- then flatten the emissions from those into a single Observable +-- @arg {function} callback - The function to transform values from the original Observable. +-- @returns {Observable} +function Observable:flatMap(callback) + callback = callback or util.identity + return self:map(callback):flatten() +end diff --git a/framework/lualib/thirdparty/rx/operators/flatMapLatest.lua b/framework/lualib/thirdparty/rx/operators/flatMapLatest.lua new file mode 100755 index 0000000..ec31111 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/flatMapLatest.lua @@ -0,0 +1,47 @@ +local Observable = require "rx.observable" +local Subscription = require "rx.subscription" +local util = require "rx.util" + +--- Returns a new Observable that uses a callback to create Observables from the values produced by +-- the source, then produces values from the most recent of these Observables. +-- @arg {function=identity} callback - The function used to convert values to Observables. +-- @returns {Observable} +function Observable:flatMapLatest(callback) + callback = callback or util.identity + return Observable.create(function(observer) + local innerSubscription + + local function onNext(...) + observer:onNext(...) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + local function subscribeInner(...) + if innerSubscription then + innerSubscription:unsubscribe() + end + + return util.tryWithObserver(observer, function(...) + innerSubscription = callback(...):subscribe(onNext, onError) + end, ...) + end + + local subscription = self:subscribe(subscribeInner, onError, onCompleted) + return Subscription.create(function() + if innerSubscription then + innerSubscription:unsubscribe() + end + + if subscription then + subscription:unsubscribe() + end + end) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/flatten.lua b/framework/lualib/thirdparty/rx/operators/flatten.lua new file mode 100755 index 0000000..e8d3d25 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/flatten.lua @@ -0,0 +1,40 @@ +local Observable = require "rx.observable" +local Subscription = require "rx.subscription" + +--- Returns a new Observable that subscribes to the Observables produced by the original and +-- produces their values. +-- @returns {Observable} +function Observable:flatten() + return Observable.create(function(observer) + local subscriptions = {} + local remaining = 1 + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + remaining = remaining - 1 + if remaining == 0 then + return observer:onCompleted() + end + end + + local function onNext(observable) + local function innerOnNext(...) + observer:onNext(...) + end + + remaining = remaining + 1 + local subscription = observable:subscribe(innerOnNext, onError, onCompleted) + subscriptions[#subscriptions + 1] = subscription + end + + subscriptions[#subscriptions + 1] = self:subscribe(onNext, onError, onCompleted) + return Subscription.create(function() + for i = 1, #subscriptions do + subscriptions[i]:unsubscribe() + end + end) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/ignoreElements.lua b/framework/lualib/thirdparty/rx/operators/ignoreElements.lua new file mode 100755 index 0000000..9e534de --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/ignoreElements.lua @@ -0,0 +1,18 @@ +local Observable = require "rx.observable" + +--- Returns an Observable that terminates when the source terminates but does not produce any +-- elements. +-- @returns {Observable} +function Observable:ignoreElements() + return Observable.create(function(observer) + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(nil, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/last.lua b/framework/lualib/thirdparty/rx/operators/last.lua new file mode 100755 index 0000000..19b1b2d --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/last.lua @@ -0,0 +1,30 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns a new Observable that only produces the last result of the original. +-- @returns {Observable} +function Observable:last() + return Observable.create(function(observer) + local value + local empty = true + + local function onNext(...) + value = {...} + empty = false + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + if not empty then + observer:onNext(util.unpack(value or {})) + end + + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/map.lua b/framework/lualib/thirdparty/rx/operators/map.lua new file mode 100755 index 0000000..2f9cd62 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/map.lua @@ -0,0 +1,27 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns a new Observable that produces the values of the original transformed by a function. +-- @arg {function} callback - The function to transform values from the original Observable. +-- @returns {Observable} +function Observable:map(callback) + return Observable.create(function(observer) + callback = callback or util.identity + + local function onNext(...) + return util.tryWithObserver(observer, function(...) + return observer:onNext(callback(...)) + end, ...) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/max.lua b/framework/lualib/thirdparty/rx/operators/max.lua new file mode 100755 index 0000000..07b414d --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/max.lua @@ -0,0 +1,7 @@ +local Observable = require "rx.observable" + +--- Returns a new Observable that produces the maximum value produced by the original. +-- @returns {Observable} +function Observable:max() + return self:reduce(math.max) +end diff --git a/framework/lualib/thirdparty/rx/operators/merge.lua b/framework/lualib/thirdparty/rx/operators/merge.lua new file mode 100755 index 0000000..8d07111 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/merge.lua @@ -0,0 +1,46 @@ +local Observable = require "rx.observable" +local Subscription = require "rx.subscription" + +--- Returns a new Observable that produces the values produced by all the specified Observables in +-- the order they are produced. +-- @arg {Observable...} sources - One or more Observables to merge. +-- @returns {Observable} +function Observable:merge(...) + local sources = {...} + table.insert(sources, 1, self) + + return Observable.create(function(observer) + local completed = {} + local subscriptions = {} + + local function onNext(...) + return observer:onNext(...) + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted(i) + return function() + table.insert(completed, i) + + if #completed == #sources then + observer:onCompleted() + end + end + end + + for i = 1, #sources do + subscriptions[i] = sources[i]:subscribe(onNext, onError, onCompleted(i)) + end + + return Subscription.create(function() + for i = 1, #sources do + if subscriptions[i] then + subscriptions[i]:unsubscribe() + end + end + end) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/min.lua b/framework/lualib/thirdparty/rx/operators/min.lua new file mode 100755 index 0000000..6a35213 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/min.lua @@ -0,0 +1,7 @@ +local Observable = require "rx.observable" + +--- Returns a new Observable that produces the minimum value produced by the original. +-- @returns {Observable} +function Observable:min() + return self:reduce(math.min) +end diff --git a/framework/lualib/thirdparty/rx/operators/pack.lua b/framework/lualib/thirdparty/rx/operators/pack.lua new file mode 100755 index 0000000..6bf9a73 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/pack.lua @@ -0,0 +1,8 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns an Observable that produces the values of the original inside tables. +-- @returns {Observable} +function Observable:pack() + return self:map(util.pack) +end diff --git a/framework/lualib/thirdparty/rx/operators/partition.lua b/framework/lualib/thirdparty/rx/operators/partition.lua new file mode 100755 index 0000000..fb8c29f --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/partition.lua @@ -0,0 +1,10 @@ +local Observable = require "rx.observable" + +--- Returns two Observables: one that produces values for which the predicate returns truthy for, +-- and another that produces values for which the predicate returns falsy. +-- @arg {function} predicate - The predicate used to partition the values. +-- @returns {Observable} +-- @returns {Observable} +function Observable:partition(predicate) + return self:filter(predicate), self:reject(predicate) +end diff --git a/framework/lualib/thirdparty/rx/operators/pluck.lua b/framework/lualib/thirdparty/rx/operators/pluck.lua new file mode 100755 index 0000000..8509d32 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/pluck.lua @@ -0,0 +1,32 @@ +local Observable = require "rx.observable" + +--- Returns a new Observable that produces values computed by extracting the given keys from the +-- tables produced by the original. +-- @arg {string...} keys - The key to extract from the table. Multiple keys can be specified to +-- recursively pluck values from nested tables. +-- @returns {Observable} +function Observable:pluck(key, ...) + if not key then + return self + end + + if type(key) ~= "string" and type(key) ~= "number" then + return Observable.throw("pluck key must be a string") + end + + return Observable.create(function(observer) + local function onNext(t) + return observer:onNext(t[key]) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end):pluck(...) +end diff --git a/framework/lualib/thirdparty/rx/operators/reduce.lua b/framework/lualib/thirdparty/rx/operators/reduce.lua new file mode 100755 index 0000000..c907c09 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/reduce.lua @@ -0,0 +1,38 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns a new Observable that produces a single value computed by accumulating the results of +-- running a function on each value produced by the original Observable. +-- @arg {function} accumulator - Accumulates the values of the original Observable. Will be passed +-- the return value of the last call as the first argument and the +-- current values as the rest of the arguments. +-- @arg {*} seed - A value to pass to the accumulator the first time it is run. +-- @returns {Observable} +function Observable:reduce(accumulator, seed) + return Observable.create(function(observer) + local result = seed + local first = true + + local function onNext(...) + if first and seed == nil then + result = ... + first = false + else + return util.tryWithObserver(observer, function(...) + result = accumulator(result, ...) + end, ...) + end + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + observer:onNext(result) + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/reject.lua b/framework/lualib/thirdparty/rx/operators/reject.lua new file mode 100755 index 0000000..e78a436 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/reject.lua @@ -0,0 +1,30 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns a new Observable that produces values from the original which do not satisfy a +-- predicate. +-- @arg {function} predicate - The predicate used to reject values. +-- @returns {Observable} +function Observable:reject(predicate) + predicate = predicate or util.identity + + return Observable.create(function(observer) + local function onNext(...) + util.tryWithObserver(observer, function(...) + if not predicate(...) then + return observer:onNext(...) + end + end, ...) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/retry.lua b/framework/lualib/thirdparty/rx/operators/retry.lua new file mode 100755 index 0000000..478eb08 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/retry.lua @@ -0,0 +1,35 @@ +local Observable = require "rx.observable" + +--- Returns an Observable that restarts in the event of an error. +-- @arg {number=} count - The maximum number of times to retry. If left unspecified, an infinite +-- number of retries will be attempted. +-- @returns {Observable} +function Observable:retry(count) + return Observable.create(function(observer) + local subscription + local retries = 0 + + local function onNext(...) + return observer:onNext(...) + end + + local function onCompleted() + return observer:onCompleted() + end + + local function onError(message) + if subscription then + subscription:unsubscribe() + end + + retries = retries + 1 + if count and retries > count then + return observer:onError(message) + end + + subscription = self:subscribe(onNext, onError, onCompleted) + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/sample.lua b/framework/lualib/thirdparty/rx/operators/sample.lua new file mode 100755 index 0000000..335b83e --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/sample.lua @@ -0,0 +1,47 @@ +local Observable = require "rx.observable" +local Subscription = require "rx.subscription" +local util = require "rx.util" + +--- Returns a new Observable that produces its most recent value every time the specified observable +-- produces a value. +-- @arg {Observable} sampler - The Observable that is used to sample values from this Observable. +-- @returns {Observable} +function Observable:sample(sampler) + if not sampler then + error("Expected an Observable") + end + + return Observable.create(function(observer) + local latest = {} + + local function setLatest(...) + latest = util.pack(...) + end + + local function onNext() + if #latest > 0 then + return observer:onNext(util.unpack(latest)) + end + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + local sourceSubscription = self:subscribe(setLatest, onError) + local sampleSubscription = sampler:subscribe(onNext, onError, onCompleted) + + return Subscription.create(function() + if sourceSubscription then + sourceSubscription:unsubscribe() + end + if sampleSubscription then + sampleSubscription:unsubscribe() + end + end) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/scan.lua b/framework/lualib/thirdparty/rx/operators/scan.lua new file mode 100755 index 0000000..c18a3b8 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/scan.lua @@ -0,0 +1,39 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns a new Observable that produces values computed by accumulating the results of running a +-- function on each value produced by the original Observable. +-- @arg {function} accumulator - Accumulates the values of the original Observable. Will be passed +-- the return value of the last call as the first argument and the +-- current values as the rest of the arguments. Each value returned +-- from this function will be emitted by the Observable. +-- @arg {*} seed - A value to pass to the accumulator the first time it is run. +-- @returns {Observable} +function Observable:scan(accumulator, seed) + return Observable.create(function(observer) + local result = seed + local first = true + + local function onNext(...) + if first and seed == nil then + result = ... + first = false + else + return util.tryWithObserver(observer, function(...) + result = accumulator(result, ...) + observer:onNext(result) + end, ...) + end + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/skip.lua b/framework/lualib/thirdparty/rx/operators/skip.lua new file mode 100755 index 0000000..3f179f0 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/skip.lua @@ -0,0 +1,31 @@ +local Observable = require "rx.observable" + +--- Returns a new Observable that skips over a specified number of values produced by the original +-- and produces the rest. +-- @arg {number=1} n - The number of values to ignore. +-- @returns {Observable} +function Observable:skip(n) + n = n or 1 + + return Observable.create(function(observer) + local i = 1 + + local function onNext(...) + if i > n then + observer:onNext(...) + else + i = i + 1 + end + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/skipLast.lua b/framework/lualib/thirdparty/rx/operators/skipLast.lua new file mode 100755 index 0000000..a5ba480 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/skipLast.lua @@ -0,0 +1,38 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns an Observable that omits a specified number of values from the end of the original +-- Observable. +-- @arg {number} count - The number of items to omit from the end. +-- @returns {Observable} +function Observable:skipLast(count) + if not count or type(count) ~= "number" then + error("Expected a number") + end + + local buffer = {} + return Observable.create(function(observer) + local function emit() + if #buffer > count and buffer[1] then + local values = table.remove(buffer, 1) + observer:onNext(util.unpack(values)) + end + end + + local function onNext(...) + emit() + table.insert(buffer, util.pack(...)) + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + emit() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/skipUntil.lua b/framework/lualib/thirdparty/rx/operators/skipUntil.lua new file mode 100755 index 0000000..f4c8a02 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/skipUntil.lua @@ -0,0 +1,36 @@ +local Observable = require "rx.observable" + +--- Returns a new Observable that skips over values produced by the original until the specified +-- Observable produces a value. +-- @arg {Observable} other - The Observable that triggers the production of values. +-- @returns {Observable} +function Observable:skipUntil(other) + return Observable.create(function(observer) + local triggered = false + local function trigger() + triggered = true + end + + other:subscribe(trigger, trigger, trigger) + + local function onNext(...) + if triggered then + observer:onNext(...) + end + end + + local function onError() + if triggered then + observer:onError() + end + end + + local function onCompleted() + if triggered then + observer:onCompleted() + end + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/skipWhile.lua b/framework/lualib/thirdparty/rx/operators/skipWhile.lua new file mode 100755 index 0000000..9c8a060 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/skipWhile.lua @@ -0,0 +1,35 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns a new Observable that skips elements until the predicate returns falsy for one of them. +-- @arg {function} predicate - The predicate used to continue skipping values. +-- @returns {Observable} +function Observable:skipWhile(predicate) + predicate = predicate or util.identity + + return Observable.create(function(observer) + local skipping = true + + local function onNext(...) + if skipping then + util.tryWithObserver(observer, function(...) + skipping = predicate(...) + end, ...) + end + + if not skipping then + return observer:onNext(...) + end + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/startWith.lua b/framework/lualib/thirdparty/rx/operators/startWith.lua new file mode 100755 index 0000000..68d90ef --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/startWith.lua @@ -0,0 +1,15 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns a new Observable that produces the specified values followed by all elements produced by +-- the source Observable. +-- @arg {*...} values - The values to produce before the Observable begins producing values +-- normally. +-- @returns {Observable} +function Observable:startWith(...) + local values = util.pack(...) + return Observable.create(function(observer) + observer:onNext(util.unpack(values)) + return self:subscribe(observer) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/sum.lua b/framework/lualib/thirdparty/rx/operators/sum.lua new file mode 100755 index 0000000..0e79fdc --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/sum.lua @@ -0,0 +1,10 @@ +local Observable = require "rx.observable" + +--- Returns an Observable that produces a single value representing the sum of the values produced +-- by the original. +-- @returns {Observable} +function Observable:sum() + return self:reduce(function(x, y) + return x + y + end, 0) +end diff --git a/framework/lualib/thirdparty/rx/operators/switch.lua b/framework/lualib/thirdparty/rx/operators/switch.lua new file mode 100755 index 0000000..8b93873 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/switch.lua @@ -0,0 +1,42 @@ +local Observable = require "rx.observable" +local Subscription = require "rx.subscription" + +--- Given an Observable that produces Observables, returns an Observable that produces the values +-- produced by the most recently produced Observable. +-- @returns {Observable} +function Observable:switch() + return Observable.create(function(observer) + local innerSubscription + + local function onNext(...) + return observer:onNext(...) + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + local function switch(source) + if innerSubscription then + innerSubscription:unsubscribe() + end + + innerSubscription = source:subscribe(onNext, onError, nil) + end + + local subscription = self:subscribe(switch, onError, onCompleted) + return Subscription.create(function() + if innerSubscription then + innerSubscription:unsubscribe() + end + + if subscription then + subscription:unsubscribe() + end + end) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/take.lua b/framework/lualib/thirdparty/rx/operators/take.lua new file mode 100755 index 0000000..09dbae9 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/take.lua @@ -0,0 +1,42 @@ +local Observable = require "rx.observable" + +--- Returns a new Observable that only produces the first n results of the original. +-- @arg {number=1} n - The number of elements to produce before completing. +-- @returns {Observable} +function Observable:take(n) + n = n or 1 + + return Observable.create(function(observer) + local subscription + if n <= 0 then + observer:onCompleted() + return + end + + local i = 1 + + local function onNext(...) + observer:onNext(...) + + i = i + 1 + + if i > n then + if subscription then + subscription:unsubscribe() + end + observer:onCompleted() + end + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + subscription = self:subscribe(onNext, onError, onCompleted) + return subscription + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/takeLast.lua b/framework/lualib/thirdparty/rx/operators/takeLast.lua new file mode 100755 index 0000000..2a036f5 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/takeLast.lua @@ -0,0 +1,36 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns an Observable that produces a specified number of elements from the end of a source +-- Observable. +-- @arg {number} count - The number of elements to produce. +-- @returns {Observable} +function Observable:takeLast(count) + if not count or type(count) ~= "number" then + error("Expected a number") + end + + return Observable.create(function(observer) + local buffer = {} + + local function onNext(...) + table.insert(buffer, util.pack(...)) + if #buffer > count then + table.remove(buffer, 1) + end + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + for i = 1, #buffer do + observer:onNext(util.unpack(buffer[i])) + end + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/takeUntil.lua b/framework/lualib/thirdparty/rx/operators/takeUntil.lua new file mode 100755 index 0000000..7731573 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/takeUntil.lua @@ -0,0 +1,29 @@ +local Observable = require "rx.observable" + +--- Returns a new Observable that completes when the specified Observable fires. +-- @arg {Observable} other - The Observable that triggers completion of the original. +-- @returns {Observable} +function Observable:takeUntil(other) + return Observable.create(function(observer) + local subscription + local function onNext(...) + return observer:onNext(...) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + if subscription then + subscription:unsubscribe() + end + return observer:onCompleted() + end + + other:subscribe(onCompleted, onCompleted, onCompleted) + + subscription = self:subscribe(onNext, onError, onCompleted) + return subscription + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/takeWhile.lua b/framework/lualib/thirdparty/rx/operators/takeWhile.lua new file mode 100755 index 0000000..d39348c --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/takeWhile.lua @@ -0,0 +1,42 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns a new Observable that produces elements until the predicate returns falsy. +-- @arg {function} predicate - The predicate used to continue production of values. +-- @returns {Observable} +function Observable:takeWhile(predicate) + predicate = predicate or util.identity + + return Observable.create(function(observer) + local taking = true + local subscription + + local function onNext(...) + if taking then + util.tryWithObserver(observer, function(...) + taking = predicate(...) + end, ...) + + if taking then + return observer:onNext(...) + else + if subscription then + subscription:unsubscribe() + end + return observer:onCompleted() + end + end + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + subscription = self:subscribe(onNext, onError, onCompleted) + return subscription + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/tap.lua b/framework/lualib/thirdparty/rx/operators/tap.lua new file mode 100755 index 0000000..8a7b012 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/tap.lua @@ -0,0 +1,42 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Runs a function each time this Observable has activity. Similar to subscribe but does not +-- create a subscription. +-- @arg {function=} onNext - Run when the Observable produces values. +-- @arg {function=} onError - Run when the Observable encounters a problem. +-- @arg {function=} onCompleted - Run when the Observable completes. +-- @returns {Observable} +function Observable:tap(_onNext, _onError, _onCompleted) + _onNext = _onNext or util.noop + _onError = _onError or util.noop + _onCompleted = _onCompleted or util.noop + + return Observable.create(function(observer) + local function onNext(...) + util.tryWithObserver(observer, function(...) + _onNext(...) + end, ...) + + return observer:onNext(...) + end + + local function onError(message) + util.tryWithObserver(observer, function() + _onError(message) + end) + + return observer:onError(message) + end + + local function onCompleted() + util.tryWithObserver(observer, function() + _onCompleted() + end) + + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/unpack.lua b/framework/lualib/thirdparty/rx/operators/unpack.lua new file mode 100755 index 0000000..65fcbd3 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/unpack.lua @@ -0,0 +1,8 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns an Observable that unpacks the tables produced by the original. +-- @returns {Observable} +function Observable:unpack() + return self:map(util.unpack) +end diff --git a/framework/lualib/thirdparty/rx/operators/unwrap.lua b/framework/lualib/thirdparty/rx/operators/unwrap.lua new file mode 100755 index 0000000..b87c2e7 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/unwrap.lua @@ -0,0 +1,25 @@ +local Observable = require "rx.observable" + +--- Returns an Observable that takes any values produced by the original that consist of multiple +-- return values and produces each value individually. +-- @returns {Observable} +function Observable:unwrap() + return Observable.create(function(observer) + local function onNext(...) + local values = {...} + for i = 1, #values do + observer:onNext(values[i]) + end + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/window.lua b/framework/lualib/thirdparty/rx/operators/window.lua new file mode 100755 index 0000000..ad34d96 --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/window.lua @@ -0,0 +1,35 @@ +local Observable = require "rx.observable" +local util = require "rx.util" + +--- Returns an Observable that produces a sliding window of the values produced by the original. +-- @arg {number} size - The size of the window. The returned observable will produce this number +-- of the most recent values as multiple arguments to onNext. +-- @returns {Observable} +function Observable:window(size) + if not size or type(size) ~= "number" then + error("Expected a number") + end + + return Observable.create(function(observer) + local window = {} + + local function onNext(value) + table.insert(window, value) + + if #window >= size then + observer:onNext(util.unpack(window)) + table.remove(window, 1) + end + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted() + return observer:onCompleted() + end + + return self:subscribe(onNext, onError, onCompleted) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/with.lua b/framework/lualib/thirdparty/rx/operators/with.lua new file mode 100755 index 0000000..67b4f5b --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/with.lua @@ -0,0 +1,50 @@ +local Observable = require "rx.observable" +local Subscription = require "rx.subscription" +local util = require "rx.util" + +--- Returns an Observable that produces values from the original along with the most recently +-- produced value from all other specified Observables. Note that only the first argument from each +-- source Observable is used. +-- @arg {Observable...} sources - The Observables to include the most recent values from. +-- @returns {Observable} +function Observable:with(...) + local sources = {...} + + return Observable.create(function(observer) + local latest = setmetatable({}, { + __len = util.constant(#sources), + }) + local subscriptions = {} + + local function setLatest(i) + return function(value) + latest[i] = value + end + end + + local function onNext(value) + return observer:onNext(value, util.unpack(latest)) + end + + local function onError(e) + return observer:onError(e) + end + + local function onCompleted() + return observer:onCompleted() + end + + for i = 1, #sources do + subscriptions[i] = sources[i]:subscribe(setLatest(i), util.noop, util.noop) + end + + subscriptions[#sources + 1] = self:subscribe(onNext, onError, onCompleted) + return Subscription.create(function() + for i = 1, #sources + 1 do + if subscriptions[i] then + subscriptions[i]:unsubscribe() + end + end + end) + end) +end diff --git a/framework/lualib/thirdparty/rx/operators/zip.lua b/framework/lualib/thirdparty/rx/operators/zip.lua new file mode 100755 index 0000000..208c6fa --- /dev/null +++ b/framework/lualib/thirdparty/rx/operators/zip.lua @@ -0,0 +1,79 @@ +local Observable = require "rx.observable" +local Subscription = require "rx.subscription" +local util = require "rx.util" + +--- Returns an Observable that merges the values produced by the source Observables by grouping them +-- by their index. The first onNext event contains the first value of all of the sources, the +-- second onNext event contains the second value of all of the sources, and so on. onNext is called +-- a number of times equal to the number of values produced by the Observable that produces the +-- fewest number of values. +-- @arg {Observable...} sources - The Observables to zip. +-- @returns {Observable} +function Observable.zip(...) + local sources = util.pack(...) + local count = #sources + + return Observable.create(function(observer) + local values = {} + local active = {} + local subscriptions = {} + for i = 1, count do + values[i] = { + n = 0, + } + active[i] = true + end + + local function onNext(i) + return function(value) + table.insert(values[i], value) + values[i].n = values[i].n + 1 + + -- luacheck: ignore i + local ready = true + for i = 1, count do + if values[i].n == 0 then + ready = false + break + end + end + + if ready then + local payload = {} + + for i = 1, count do + payload[i] = table.remove(values[i], 1) + values[i].n = values[i].n - 1 + end + + observer:onNext(util.unpack(payload)) + end + end + end + + local function onError(message) + return observer:onError(message) + end + + local function onCompleted(i) + return function() + active[i] = nil + if not next(active) or values[i].n == 0 then + return observer:onCompleted() + end + end + end + + for i = 1, count do + subscriptions[i] = sources[i]:subscribe(onNext(i), onError, onCompleted(i)) + end + + return Subscription.create(function() + for i = 1, count do + if subscriptions[i] then + subscriptions[i]:unsubscribe() + end + end + end) + end) +end diff --git a/framework/lualib/thirdparty/rx/schedulers/cooperativescheduler.lua b/framework/lualib/thirdparty/rx/schedulers/cooperativescheduler.lua new file mode 100755 index 0000000..b39f557 --- /dev/null +++ b/framework/lualib/thirdparty/rx/schedulers/cooperativescheduler.lua @@ -0,0 +1,87 @@ +local util = require "rx.util" +local Subscription = require "rx.subscription" + +--- @class CooperativeScheduler +-- @description Manages Observables using coroutines and a virtual clock that must be updated +-- manually. +local CooperativeScheduler = {} +CooperativeScheduler.__index = CooperativeScheduler +CooperativeScheduler.__tostring = util.constant("CooperativeScheduler") + +--- Creates a new CooperativeScheduler. +-- @arg {number=0} currentTime - A time to start the scheduler at. +-- @returns {CooperativeScheduler} +function CooperativeScheduler.create(currentTime) + local self = { + tasks = {}, + currentTime = currentTime or 0, + } + + return setmetatable(self, CooperativeScheduler) +end + +--- Schedules a function to be run after an optional delay. Returns a subscription that will stop +-- the action from running. +-- @arg {function} action - The function to execute. Will be converted into a coroutine. The +-- coroutine may yield execution back to the scheduler with an optional +-- number, which will put it to sleep for a time period. +-- @arg {number=0} delay - Delay execution of the action by a virtual time period. +-- @returns {Subscription} +function CooperativeScheduler:schedule(action, delay) + local task = { + thread = coroutine.create(action), + due = self.currentTime + (delay or 0), + } + + table.insert(self.tasks, task) + + return Subscription.create(function() + return self:unschedule(task) + end) +end + +function CooperativeScheduler:unschedule(task) + for i = 1, #self.tasks do + if self.tasks[i] == task then + table.remove(self.tasks, i) + end + end +end + +--- Triggers an update of the CooperativeScheduler. The clock will be advanced and the scheduler +-- will run any coroutines that are due to be run. +-- @arg {number=0} delta - An amount of time to advance the clock by. It is common to pass in the +-- time in seconds or milliseconds elapsed since this function was last +-- called. +function CooperativeScheduler:update(delta) + self.currentTime = self.currentTime + (delta or 0) + + local i = 1 + while i <= #self.tasks do + local task = self.tasks[i] + + if self.currentTime >= task.due then + local success, delay = coroutine.resume(task.thread) + + if coroutine.status(task.thread) == "dead" then + table.remove(self.tasks, i) + else + task.due = math.max(task.due + (delay or 0), self.currentTime) + i = i + 1 + end + + if not success then + error(delay) + end + else + i = i + 1 + end + end +end + +--- Returns whether or not the CooperativeScheduler's queue is empty. +function CooperativeScheduler:isEmpty() + return not next(self.tasks) +end + +return CooperativeScheduler diff --git a/framework/lualib/thirdparty/rx/schedulers/immediatescheduler.lua b/framework/lualib/thirdparty/rx/schedulers/immediatescheduler.lua new file mode 100755 index 0000000..8607864 --- /dev/null +++ b/framework/lualib/thirdparty/rx/schedulers/immediatescheduler.lua @@ -0,0 +1,22 @@ +local util = require "rx.util" + +--- @class ImmediateScheduler +-- @description Schedules Observables by running all operations immediately. +local ImmediateScheduler = {} +ImmediateScheduler.__index = ImmediateScheduler +ImmediateScheduler.__tostring = util.constant("ImmediateScheduler") + +--- Creates a new ImmediateScheduler. +-- @returns {ImmediateScheduler} +function ImmediateScheduler.create() + return setmetatable({}, ImmediateScheduler) +end + +--- Schedules a function to be run on the scheduler. It is executed immediately. +-- @arg {function} action - The function to execute. +function ImmediateScheduler:schedule(action) + local _ = self + action() +end + +return ImmediateScheduler diff --git a/framework/lualib/thirdparty/rx/schedulers/timeoutscheduler.lua b/framework/lualib/thirdparty/rx/schedulers/timeoutscheduler.lua new file mode 100755 index 0000000..e4fdb70 --- /dev/null +++ b/framework/lualib/thirdparty/rx/schedulers/timeoutscheduler.lua @@ -0,0 +1,29 @@ +local Subscription = require "rx.subscription" +local util = require "rx.util" + +--- @class TimeoutScheduler +-- @description A scheduler that uses luvit's timer library to schedule events on an event loop. +local TimeoutScheduler = {} +TimeoutScheduler.__index = TimeoutScheduler +TimeoutScheduler.__tostring = util.constant("TimeoutScheduler") + +--- Creates a new TimeoutScheduler. +-- @returns {TimeoutScheduler} +function TimeoutScheduler.create() + return setmetatable({}, TimeoutScheduler) +end + +--- Schedules an action to run at a future point in time. +-- @arg {function} action - The action to run. +-- @arg {number=0} delay - The delay, in milliseconds. +-- @returns {Subscription} +function TimeoutScheduler:schedule(action, delay, ...) + local _ = self + local timer = require "rx.timer" + local handle = timer.setTimeout(delay, action, ...) + return Subscription.create(function() + timer.clearTimeout(handle) + end) +end + +return TimeoutScheduler diff --git a/framework/lualib/thirdparty/rx/subjects/asyncsubject.lua b/framework/lualib/thirdparty/rx/subjects/asyncsubject.lua new file mode 100755 index 0000000..5382c84 --- /dev/null +++ b/framework/lualib/thirdparty/rx/subjects/asyncsubject.lua @@ -0,0 +1,103 @@ +local Observable = require "rx.observable" +local Observer = require "rx.observer" +local Subscription = require "rx.subscription" +local util = require "rx.util" + +--- @class AsyncSubject +-- @description AsyncSubjects are subjects that produce either no values or a single value. If +-- multiple values are produced via onNext, only the last one is used. If onError is called, then +-- no value is produced and onError is called on any subscribed Observers. If an Observer +-- subscribes and the AsyncSubject has already terminated, the Observer will immediately receive the +-- value or the error. +local AsyncSubject = setmetatable({}, Observable) +AsyncSubject.__index = AsyncSubject +AsyncSubject.__tostring = util.constant("AsyncSubject") + +--- Creates a new AsyncSubject. +-- @returns {AsyncSubject} +function AsyncSubject.create() + local self = { + observers = {}, + stopped = false, + value = nil, + errorMessage = nil, + } + + return setmetatable(self, AsyncSubject) +end + +--- Creates a new Observer and attaches it to the AsyncSubject. +-- @arg {function|table} onNext|observer - A function called when the AsyncSubject produces a value +-- or an existing Observer to attach to the AsyncSubject. +-- @arg {function} onError - Called when the AsyncSubject terminates due to an error. +-- @arg {function} onCompleted - Called when the AsyncSubject completes normally. +function AsyncSubject:subscribe(onNext, onError, onCompleted) + local observer + + if util.isa(onNext, Observer) then + observer = onNext + else + observer = Observer.create(onNext, onError, onCompleted) + end + + if self.value then + observer:onNext(util.unpack(self.value)) + observer:onCompleted() + return + elseif self.errorMessage then + observer:onError(self.errorMessage) + return + end + + table.insert(self.observers, observer) + + return Subscription.create(function() + for i = 1, #self.observers do + if self.observers[i] == observer then + table.remove(self.observers, i) + return + end + end + end) +end + +--- Pushes zero or more values to the AsyncSubject. +-- @arg {*...} values +function AsyncSubject:onNext(...) + if not self.stopped then + self.value = util.pack(...) + end +end + +--- Signal to all Observers that an error has occurred. +-- @arg {string=} message - A string describing what went wrong. +function AsyncSubject:onError(message) + if not self.stopped then + self.errorMessage = message + + for i = 1, #self.observers do + self.observers[i]:onError(self.errorMessage) + end + + self.stopped = true + end +end + +--- Signal to all Observers that the AsyncSubject will not produce any more values. +function AsyncSubject:onCompleted() + if not self.stopped then + for i = 1, #self.observers do + if self.value then + self.observers[i]:onNext(util.unpack(self.value)) + end + + self.observers[i]:onCompleted() + end + + self.stopped = true + end +end + +AsyncSubject.__call = AsyncSubject.onNext + +return AsyncSubject diff --git a/framework/lualib/thirdparty/rx/subjects/behaviorsubject.lua b/framework/lualib/thirdparty/rx/subjects/behaviorsubject.lua new file mode 100755 index 0000000..8053fc5 --- /dev/null +++ b/framework/lualib/thirdparty/rx/subjects/behaviorsubject.lua @@ -0,0 +1,69 @@ +local Subject = require "rx.subjects.subject" +local Observer = require "rx.observer" +local util = require "rx.util" + +--- @class BehaviorSubject +-- @description A Subject that tracks its current value. Provides an accessor to retrieve the most +-- recent pushed value, and all subscribers immediately receive the latest value. +local BehaviorSubject = setmetatable({}, Subject) +BehaviorSubject.__index = BehaviorSubject +BehaviorSubject.__tostring = util.constant("BehaviorSubject") + +--- Creates a new BehaviorSubject. +-- @arg {*...} value - The initial values. +-- @returns {BehaviorSubject} +function BehaviorSubject.create(...) + local self = { + observers = {}, + stopped = false, + } + + if select("#", ...) > 0 then + self.value = util.pack(...) + end + + return setmetatable(self, BehaviorSubject) +end + +--- Creates a new Observer and attaches it to the BehaviorSubject. Immediately broadcasts the most +-- recent value to the Observer. +-- @arg {function} onNext - Called when the BehaviorSubject produces a value. +-- @arg {function} onError - Called when the BehaviorSubject terminates due to an error. +-- @arg {function} onCompleted - Called when the BehaviorSubject completes normally. +function BehaviorSubject:subscribe(onNext, onError, onCompleted) + local observer + + if util.isa(onNext, Observer) then + observer = onNext + else + observer = Observer.create(onNext, onError, onCompleted) + end + + local subscription = Subject.subscribe(self, observer) + + if self.value then + observer:onNext(util.unpack(self.value)) + end + + return subscription +end + +--- Pushes zero or more values to the BehaviorSubject. They will be broadcasted to all Observers. +-- @arg {*...} values +function BehaviorSubject:onNext(...) + self.value = util.pack(...) + return Subject.onNext(self, ...) +end + +--- Returns the last value emitted by the BehaviorSubject, or the initial value passed to the +-- constructor if nothing has been emitted yet. +-- @returns {*...} +function BehaviorSubject:getValue() + if self.value ~= nil then + return util.unpack(self.value) + end +end + +BehaviorSubject.__call = BehaviorSubject.onNext + +return BehaviorSubject diff --git a/framework/lualib/thirdparty/rx/subjects/replaysubject.lua b/framework/lualib/thirdparty/rx/subjects/replaysubject.lua new file mode 100755 index 0000000..0ad90b8 --- /dev/null +++ b/framework/lualib/thirdparty/rx/subjects/replaysubject.lua @@ -0,0 +1,63 @@ +local Subject = require "rx.subjects.subject" +local Observer = require "rx.observer" +local util = require "rx.util" + +--- @class ReplaySubject +-- @description A Subject that provides new Subscribers with some or all of the most recently +-- produced values upon subscription. +local ReplaySubject = setmetatable({}, Subject) +ReplaySubject.__index = ReplaySubject +ReplaySubject.__tostring = util.constant("ReplaySubject") + +--- Creates a new ReplaySubject. +-- @arg {number=} bufferSize - The number of values to send to new subscribers. If nil, an infinite +-- buffer is used (note that this could lead to memory issues). +-- @returns {ReplaySubject} +function ReplaySubject.create(n) + local self = { + observers = {}, + stopped = false, + buffer = {}, + bufferSize = n, + } + + return setmetatable(self, ReplaySubject) +end + +--- Creates a new Observer and attaches it to the ReplaySubject. Immediately broadcasts the most +-- contents of the buffer to the Observer. +-- @arg {function} onNext - Called when the ReplaySubject produces a value. +-- @arg {function} onError - Called when the ReplaySubject terminates due to an error. +-- @arg {function} onCompleted - Called when the ReplaySubject completes normally. +function ReplaySubject:subscribe(onNext, onError, onCompleted) + local observer + + if util.isa(onNext, Observer) then + observer = onNext + else + observer = Observer.create(onNext, onError, onCompleted) + end + + local subscription = Subject.subscribe(self, observer) + + for i = 1, #self.buffer do + observer:onNext(util.unpack(self.buffer[i])) + end + + return subscription +end + +--- Pushes zero or more values to the ReplaySubject. They will be broadcasted to all Observers. +-- @arg {*...} values +function ReplaySubject:onNext(...) + table.insert(self.buffer, util.pack(...)) + if self.bufferSize and #self.buffer > self.bufferSize then + table.remove(self.buffer, 1) + end + + return Subject.onNext(self, ...) +end + +ReplaySubject.__call = ReplaySubject.onNext + +return ReplaySubject diff --git a/framework/lualib/thirdparty/rx/subjects/subject.lua b/framework/lualib/thirdparty/rx/subjects/subject.lua new file mode 100755 index 0000000..7485bbe --- /dev/null +++ b/framework/lualib/thirdparty/rx/subjects/subject.lua @@ -0,0 +1,86 @@ +local Observable = require "rx.observable" +local Observer = require "rx.observer" +local Subscription = require "rx.subscription" +local util = require "rx.util" + +--- @class Subject +-- @description Subjects function both as an Observer and as an Observable. Subjects inherit all +-- Observable functions, including subscribe. Values can also be pushed to the Subject, which will +-- be broadcasted to any subscribed Observers. +local Subject = setmetatable({}, Observable) +Subject.__index = Subject +Subject.__tostring = util.constant("Subject") + +--- Creates a new Subject. +-- @returns {Subject} +function Subject.create() + local self = { + observers = {}, + stopped = false, + } + + return setmetatable(self, Subject) +end + +--- Creates a new Observer and attaches it to the Subject. +-- @arg {function|table} onNext|observer - A function called when the Subject produces a value or +-- an existing Observer to attach to the Subject. +-- @arg {function} onError - Called when the Subject terminates due to an error. +-- @arg {function} onCompleted - Called when the Subject completes normally. +function Subject:subscribe(onNext, onError, onCompleted) + local observer + + if util.isa(onNext, Observer) then + observer = onNext + else + observer = Observer.create(onNext, onError, onCompleted) + end + + table.insert(self.observers, observer) + + return Subscription.create(function() + for i = 1, #self.observers do + if self.observers[i] == observer then + table.remove(self.observers, i) + return + end + end + end) +end + +--- Pushes zero or more values to the Subject. They will be broadcasted to all Observers. +-- @arg {*...} values +function Subject:onNext(...) + if not self.stopped then + for i = #self.observers, 1, -1 do + self.observers[i]:onNext(...) + end + end +end + +--- Signal to all Observers that an error has occurred. +-- @arg {string=} message - A string describing what went wrong. +function Subject:onError(message) + if not self.stopped then + for i = #self.observers, 1, -1 do + self.observers[i]:onError(message) + end + + self.stopped = true + end +end + +--- Signal to all Observers that the Subject will not produce any more values. +function Subject:onCompleted() + if not self.stopped then + for i = #self.observers, 1, -1 do + self.observers[i]:onCompleted() + end + + self.stopped = true + end +end + +Subject.__call = Subject.onNext + +return Subject diff --git a/framework/lualib/thirdparty/rx/subscription.lua b/framework/lualib/thirdparty/rx/subscription.lua new file mode 100755 index 0000000..f7a51ed --- /dev/null +++ b/framework/lualib/thirdparty/rx/subscription.lua @@ -0,0 +1,32 @@ +local util = require "rx.util" + +--- @class Subscription +-- @description A handle representing the link between an Observer and an Observable, as well as any +-- work required to clean up after the Observable completes or the Observer unsubscribes. +local Subscription = {} +Subscription.__index = Subscription +Subscription.__tostring = util.constant("Subscription") + +--- Creates a new Subscription. +-- @arg {function=} action - The action to run when the subscription is unsubscribed. It will only +-- be run once. +-- @returns {Subscription} +function Subscription.create(action) + local self = { + action = action or util.noop, + unsubscribed = false, + } + + return setmetatable(self, Subscription) +end + +--- Unsubscribes the subscription, performing any necessary cleanup work. +function Subscription:unsubscribe() + if self.unsubscribed then + return + end + self.action(self) + self.unsubscribed = true +end + +return Subscription diff --git a/framework/lualib/thirdparty/rx/timer.lua b/framework/lualib/thirdparty/rx/timer.lua new file mode 100755 index 0000000..6068e9c --- /dev/null +++ b/framework/lualib/thirdparty/rx/timer.lua @@ -0,0 +1,2 @@ +-- TODO: implement this timer +return {} diff --git a/framework/lualib/thirdparty/rx/util.lua b/framework/lualib/thirdparty/rx/util.lua new file mode 100755 index 0000000..8c19e1f --- /dev/null +++ b/framework/lualib/thirdparty/rx/util.lua @@ -0,0 +1,34 @@ +local util = {} + +util.pack = table.pack or function(...) + return { + n = select('#', ...), + ..., + } +end +util.unpack = table.unpack or _G.unpack +util.eq = function(x, y) + return x == y +end +util.noop = function() +end +util.identity = function(x) + return x +end +util.constant = function(x) + return function() + return x + end +end +util.isa = function(object, class) + return type(object) == 'table' and getmetatable(object).__index == class +end +util.tryWithObserver = function(observer, fn, ...) + local success, result = pcall(fn, ...) + if not success then + observer:onError(result) + end + return success, result +end + +return util diff --git a/framework/lualib/thirdparty/sandbox/sandbox.lua b/framework/lualib/thirdparty/sandbox/sandbox.lua new file mode 100644 index 0000000..1718f42 --- /dev/null +++ b/framework/lualib/thirdparty/sandbox/sandbox.lua @@ -0,0 +1,170 @@ +local sandbox = { + _VERSION = "sandbox 0.5", + _DESCRIPTION = "A pure-lua solution for running untrusted Lua code.", + _URL = "https://github.com/kikito/sandbox.lua", + +} + +-- quotas don't work in LuaJIT since debug.sethook works differently there +local quota_supported = type(_G.jit) == "nil" +sandbox.quota_supported = quota_supported + +-- PUC-Rio Lua 5.1 does not support deactivation of bytecode +local bytecode_blocked = _ENV or type(_G.jit) == "table" +sandbox.bytecode_blocked = bytecode_blocked + +-- The base environment is merged with the given env option (or an empty table, if no env provided) +-- +local BASE_ENV = {} + +-- List of unsafe packages/functions: +-- +-- * string.rep: can be used to allocate millions of bytes in 1 operation +-- * {set|get}metatable: can be used to modify the metatable of global objects (strings, integers) +-- * collectgarbage: can affect performance of other systems +-- * dofile: can access the server filesystem +-- * _G: It has access to everything. It can be mocked to other things though. +-- * load{file|string}: All unsafe because they can grant acces to global env +-- * raw{get|set|equal}: Potentially unsafe +-- * module|require|module: Can modify the host settings +-- * string.dump: Can display confidential server info (implementation of functions) +-- * math.randomseed: Can affect the host sytem +-- * io.*, os.*: Most stuff there is unsafe, see below for exceptions + +-- Safe packages/functions below +([[ + + _VERSION assert error ipairs next pairs + pcall select tonumber tostring type unpack xpcall + + coroutine.create coroutine.resume coroutine.running coroutine.status + coroutine.wrap coroutine.yield + + math.abs math.acos math.asin math.atan math.atan2 math.ceil + math.cos math.cosh math.deg math.exp math.fmod math.floor + math.frexp math.huge math.ldexp math.log math.log10 math.max + math.min math.modf math.pi math.pow math.rad math.random + math.sin math.sinh math.sqrt math.tan math.tanh + + os.clock os.difftime os.time + + string.byte string.char string.find string.format string.gmatch + string.gsub string.len string.lower string.match string.reverse + string.sub string.upper + + table.insert table.maxn table.remove table.sort + + ]]):gsub('%S+', function(id) + local module, method = id:match('([^%.]+)%.([^%.]+)') + if module then + BASE_ENV[module] = BASE_ENV[module] or {} + BASE_ENV[module][method] = _G[module][method] + else + BASE_ENV[id] = _G[id] + end +end) + +local function protect_module(module, module_name) + return setmetatable({}, { + __index = module, + __newindex = function(_, attr_name, _) + error('Can not modify ' .. module_name .. '.' .. attr_name .. '. Protected by the sandbox.') + end, + }) +end + +('coroutine math os string table'):gsub('%S+', function(module_name) + BASE_ENV[module_name] = protect_module(BASE_ENV[module_name], module_name) +end) + +-- auxiliary functions/variables + +local string_rep = string.rep + +local function sethook(f, key, quota) + if type(debug) ~= 'table' or type(debug.sethook) ~= 'function' then + return + end + debug.sethook(f, key, quota) +end + +local function cleanup() + sethook() + string.rep = string_rep -- luacheck: no global + +end + +-- Public interface: sandbox.protect +function sandbox.protect(code, options) + options = options or {} + + local quota = false + if options.quota and not quota_supported then + error("options.quota is not supported on this environment (usually LuaJIT). Please unset options.quota") + end + if options.quota ~= false then + quota = options.quota or 500000 + end + + assert(type(code) == 'string', "expected a string") + + local passed_env = options.env or {} + local env = {} + for k, v in pairs(BASE_ENV) do + local pv = passed_env[k] + if pv ~= nil then + env[k] = pv + else + env[k] = v + end + end + setmetatable(env, { + __index = options.env, + }) + env._G = env + + local f + if bytecode_blocked then + f = assert(load(code, nil, 't', env)) + else + f = assert(loadstring(code)) + setfenv(f, env) + end + + return function(...) + + if quota and quota_supported then + local timeout = function() + cleanup() + error('Quota exceeded: ' .. tostring(quota)) + end + sethook(timeout, "", quota) + end + + string.rep = nil -- luacheck: no global + + local t = table.pack(pcall(f, ...)) + + cleanup() + + if not t[1] then + error(t[2]) + end + + return table.unpack(t, 2, t.n) + end +end + +-- Public interface: sandbox.run +function sandbox.run(code, options, ...) + return sandbox.protect(code, options)(...) +end + +-- make sandbox(f) == sandbox.protect(f) +setmetatable(sandbox, { + __call = function(_, code, o) + return sandbox.protect(code, o) + end, +}) + +return sandbox diff --git a/framework/lualib/thirdparty/snaphot/lsnapshot.lua b/framework/lualib/thirdparty/snaphot/lsnapshot.lua new file mode 100644 index 0000000..385dfda --- /dev/null +++ b/framework/lualib/thirdparty/snaphot/lsnapshot.lua @@ -0,0 +1,179 @@ +local ss = require "snapshot" +local snapshot = ss.snapshot +local str2ud = ss.str2ud + +local Root = str2ud("0") +local M = {} + +local t2simple = { + table = "(T)", + userdata = "(U)", + -- ["function"] = "(L)", + thread = "(S)", + cfunction = "(C)", + string = "(A)", +} + +local begin_s = nil +function M.start_snapshot() + begin_s = snapshot() +end + +local function reshape_snapshot(s, full_snapshot) + local reshape = {} + local function add_reshape(k, v, is_new) + local t, pk, field = string.match(v, "^([^\n]+)\n%(?([%a%d]+)%)? : ([^\n]+)") + pk = pk == "nil" and "0" or pk + pk = str2ud(pk) + local tt, size = string.match(t, "([^%s]*).* {(%d+)}") + if not tt then + error(string.format("invalid snapshot value:%s", v)) + end + t = tt + size = size and tonumber(size) or 0 + local st = t2simple[t] or string.format("(L@%s)", t) + reshape[k] = { + t = t, + size = size, + st = st, + parent_key = pk, + field = field, + item = st .. field, + fullpath = nil, + is_new = is_new, + } + if not s[pk] and not reshape[pk] then + local pv = full_snapshot[pk] + if pv then + add_reshape(pk, pv, false) + end + end + end + + for k, v in pairs(s) do + add_reshape(k, v, true) + end + + local function gen_fullname() + for _, entry in pairs(reshape) do + if not entry.fullpath then + local list = {{ + entry = entry, + item = entry.item, + }} + local map = {} + while true do + local pk = entry.parent_key + local pentry = reshape[pk] + if not pentry then + list[#list + 1] = pk == Root and "Root" or string.format("{%s}", pk) + break + elseif pentry.fullpath then + list[#list + 1] = pentry.fullpath + break + else + if map[pk] then + pentry.fullpath = pentry.item + list[#list + 1] = pentry.item + break + else + list[#list + 1] = { + entry = pentry, + item = pentry.item, + } + map[pk] = true + end + end + entry = pentry + end + + local nlist = {} + local len = #list + for i = len, 1, -1 do + nlist[#nlist + 1] = list[i] + end + + local spath = {} + for _, v in ipairs(nlist) do + if type(v) == "string" then + spath[#spath + 1] = v + else + spath[#spath + 1] = v.item + local fullpath = table.concat(spath, "->") + v.entry.fullpath = fullpath + end + end + end + end + end + gen_fullname() + + local ret = {} + for _, v in pairs(reshape) do + if v.is_new then + ret[#ret + 1] = { + type = v.t, + path = v.fullpath, + v = v.v, + st = v.st, + size = v.size, + } + end + end + return ret +end + +local function diff_snapshot(be_s, end_s) + local reshape + if not end_s then + reshape = reshape_snapshot(be_s, be_s) + else + local diff_s = {} + for k, v in pairs(end_s) do + if be_s[k] == nil then + diff_s[k] = v + end + end + reshape = reshape_snapshot(diff_s, end_s) + end + table.sort(reshape, function(a, b) + return a.size > b.size + end) + return reshape +end + +local function dump_reshape(reshape, len) + local rlen = #reshape + len = len or rlen + if len < 0 or len > rlen then + len = rlen + end + + local all_size = 0 + local diff_data = {} + + for i = 1, rlen do + local v = reshape[i] + all_size = all_size + v.size + if i <= len then + table.insert(diff_data, { + rank = i, + st = v.st, + path = v.path, + size = v.size / 1024, + }) + end + end + return rlen, diff_data +end + +function M.dstop_snapshot(len) + if not begin_s then + error("snapshot not begin") + end + local end_s = snapshot() + local reshape = diff_snapshot(end_s) + return dump_reshape(reshape, len) +end + +return M diff --git a/framework/lualib/thirdparty/sync9.lua b/framework/lualib/thirdparty/sync9.lua new file mode 100644 index 0000000..73924b7 --- /dev/null +++ b/framework/lualib/thirdparty/sync9.lua @@ -0,0 +1,314 @@ +-- https://github.com/pkulchenko/syncX +-- Implementation of Sync9 algorithm based on sync9.js from https://github.com/braid-org/braidjs +-- Copyright 2021 Paul Kulchenko +-- +local function argcheck(cond, i, f, extra) + if not cond then + error("bad argument #" .. i .. " to '" .. f .. "' (" .. extra .. ")", 0) + end +end + +local function splice(tbl, startidx, delcnt, ...) + local inscnt = select('#', ...) + local tblcnt = #tbl + argcheck(startidx >= 1, 2, "splice", "initial position must be positive") + argcheck(startidx <= tblcnt + 1, 2, "splice", "initial position must not exceed table size by more than 1") + argcheck(delcnt >= 0, 2, "splice", "delete count must be non-negative") + + -- remove excess (if any) + for _ = 1, delcnt - inscnt do + table.remove(tbl, startidx) + end + -- insert if needed + for i = 1, inscnt - delcnt do + table.insert(tbl, startidx + i - 1, (select(i, ...))) + end + -- assign the rest, as there is enough space + for i = math.max(1, inscnt - delcnt + 1), inscnt do + tbl[startidx + i - 1] = select(i, ...) + end +end + +-- modified from https://stackoverflow.com/questions/22697936/binary-search-in-javascript +local function binarySearch(tbl, comparator) + local m = 1 + local n = #tbl + while (m <= n) do + local k = math.floor((n + m) / 2) + local cmp = comparator(tbl[k]) + if (cmp > 0) then + m = k + 1 + elseif (cmp < 0) then + n = k - 1 + else + return k + end + end + return m +end + +local function spliceinto(tbl, part) + local i = binarySearch(tbl, function(x) + return part.version < x.version and -1 or 1 + end) + tbl:splice(i, 0, part) + return i +end + +local function any(tbl, condition) + for k, v in pairs(tbl) do + if condition(k, v) then + return true + end + end + return false +end + +-- traverse spaceDAG starting from node and calling `callback` for each part +local function traverse_space_dag(node, isanc, callback) + local offset = 0 + local function helper(node, version, prev) + local deleted = node.deletedby:any(function(version) + return isanc(version) + end) + -- callback may return `false` to indicate that traversal needs to be stopped + if callback(node, version, prev, offset, deleted) == false then + return false + end + if not deleted then + offset = offset + #node.elems + end + for _, part in ipairs(node.parts) do + if isanc(part.version) and helper(part, part.version) == false then + return false + end + end + if node.parts[0] and helper(node.parts[0], version, node) == false then + return false + end + end + return helper(node, node.version) +end + +local function space_dag_get(node, index, is_anc) + -- index value is 0-based + local value + local offset = 0 + traverse_space_dag(node, is_anc or function() + return true + end, function(node) + if (index - offset < #node.elems) then + value = node.elems[index - offset + 1] + return false + end + offset = offset + #node.elems + end) + return value +end + +local function space_dag_set(node, index, value, is_anc) + -- index value is 0-based + local offset = 0 + traverse_space_dag(node, is_anc or function() + return true + end, function(node) + if (index - offset < #node.elems) then + node.elems[index - offset + 1] = value + return false + end + offset = offset + #node.elems + end) +end + +local metaparts = { + __index = { + splice = splice, + slice = function(...) + return {table.unpack(...)} + end, + spliceinto = spliceinto, -- insert in the appropriate slot based on binary search by version + any = any, -- return `true` if any of the values match condition + copy = function(tbl) + return {table.unpack(tbl)} + end, + }, +} +local metadags = { + __index = { + getlength = function(node, isanc) + isanc = isanc or function() + return true + end + local count = 0 + traverse_space_dag(node, isanc, function(node) + count = count + node.elems.length + end) + return count + end, + getvalue = function(node, isanc) + isanc = isanc or function() + return true + end + local values = {} + traverse_space_dag(node, isanc, function(node, _, _, _, deleted) + if not deleted then + table.insert(values, table.concat(node.elems, "")) + end + end) + return table.concat(values) + end, + get = space_dag_get, + set = space_dag_set, + }, +} + +local function create_space_dag_node(version, elems, deletedby) + assert(not elems or type(elems) == "table") + assert(not deletedby or type(deletedby) == "table") + return setmetatable({ + version = version, -- node version as a string + elems = setmetatable(elems or {}, metaparts), -- list of elements this node stores + deletedby = setmetatable(deletedby or {}, metaparts), -- hash of versions this node is deleted by + parts = setmetatable({}, metaparts), -- list of nodes that are children of this one + -- parts[0] is a special non-versioned node that has been spliced from the elements of the curent node + }, metadags) +end + +local function space_dag_break_node(node, splitidx, newpart) + assert(splitidx >= 0) + local tail = create_space_dag_node(nil, node.elems:slice(splitidx + 1), node.deletedby:copy()) + tail.parts = node.parts + + node.elems = setmetatable(node.elems:slice(1, splitidx), getmetatable(node.elems)) + node.parts = setmetatable(newpart and {newpart} or {}, getmetatable(node.parts)) + node.parts[0] = tail + return tail +end + +-- add a patchset to a node, which will have `nodeversion` after patching +local function space_dag_add_patchset(node, nodeversion, patches, isanc) + isanc = isanc or function() + return true + end + local deleteupto = 0 -- position to delete elements up to + local deferred = {} -- list of deferred callbacks + local deletedcnt = 0 -- number of deleted elements in the current patchset + setmetatable(patches, { + __index = { + next = function(tbl) + table.remove(tbl, 1) -- remove the processed patch + deleteupto = 0 -- reset delete tracker, as it's calculated per patch + -- process deferred callbacks (for example, node splits) + while #deferred > 0 do + table.remove(deferred, 1)() + end + end, + defer = function(_, callback) + table.insert(deferred, callback) + end, + }, + }) + + local function process_patch(node, patchversion, prev, offset, isdeleted) + if #patches == 0 then + return false + end -- nothing to process further + local addidx, delcnt, val = table.unpack(patches[1]) + local hasparts = node.parts:any(function(_, part) + return isanc(part.version) + end) + -- since the patches in the patchset are processed as independent patches + -- (even though the graph is only traversed once), + -- adjust the offset for the number of deletes to use the correct position + offset = offset - deletedcnt + + -- this element is already deleted + if isdeleted then + -- this patch only adds elements at the current offset + if delcnt == 0 and addidx == offset then + if #node.elems == 0 and hasparts then + return + end + if #node.elems > 0 then + space_dag_break_node(node, 0) + end + node.parts:spliceinto(create_space_dag_node(nodeversion, val)) + patches:next() + end + return + end + + -- nothing is being deleted, but need to do an insert + if delcnt == 0 then + if addidx < offset then + return + end -- trying to insert before the current offset + local d = addidx - (offset + #node.elems) + if d > 0 then + return + end -- trying to insert after the max index + if d == 0 and hasparts then + return + end -- shortcuts the processing to add a new element to a new node to enforce the order + if d ~= 0 then + space_dag_break_node(node, addidx - offset) + end + node.parts:spliceinto(create_space_dag_node(nodeversion, val)) + patches:next() + return + end + + if deleteupto <= offset then + local d = addidx - (offset + #node.elems) + if d >= 0 then + return + end -- trying to insert at or after the max index + deleteupto = addidx + delcnt + + if val then + local newnode = create_space_dag_node(nodeversion, val) + if addidx == offset and prev then + -- defer updates, otherwise inserted nodes affect position tracking + patches:defer(function() + node.parts:spliceinto(newnode) + end) + -- fall through to the next check for `deleteupto` + else + space_dag_break_node(node, addidx - offset) + -- defer updates, otherwise inserted nodes affect position tracking + patches:defer(function() + node.parts:spliceinto(newnode) + end) + return + end + else + if addidx == offset then + -- fall through to the next check for `deleteupto` + else + space_dag_break_node(node, addidx - offset) + return + end + end + end + + if deleteupto > offset then + if deleteupto <= offset + #node.elems then + if deleteupto < offset + #node.elems then + space_dag_break_node(node, deleteupto - offset) + -- increase the number of deleted elements subtracting the number of added ones + deletedcnt = deletedcnt + #node.elems - #val + end + patches:next() + end + node.deletedby[nodeversion] = true + return + end + end + traverse_space_dag(node, isanc, process_patch) +end + +metadags.__index.addpatchset = space_dag_add_patchset + +return { + createnode = create_space_dag_node, +} diff --git a/framework/lualib/thirdparty/traceable.lua b/framework/lualib/thirdparty/traceable.lua new file mode 100644 index 0000000..7c48dde --- /dev/null +++ b/framework/lualib/thirdparty/traceable.lua @@ -0,0 +1,655 @@ +-- -- https://github.com/ayongm2/TableMonitor +-- -- https://github.com/jojo59516/traceable +-- local next = next +-- local pairs = pairs +-- local ipairs = ipairs +-- local unpack = table.unpack +-- local load = load +-- local setmetatable = setmetatable +-- local assert = assert +-- local getmetatable = getmetatable +-- local type = type +-- local tonumber = tonumber +-- ----------------------------------------------------- +-- local traceable = {} +-- local _M = traceable +-- local function merge(dst, src) +-- for k, v in next, src do +-- dst[k] = v +-- end +-- return dst +-- end +-- local function normalize_field_path(field_path) +-- return field_path:gsub("^([-?%d]+)", "[%1]"):gsub("%.([-?%d]+)", "[%1]"):gsub("^([^.%[%]]+)", "['%1']"):gsub( +-- "%.([^.%[%]]+)", +-- "['%1']" +-- ) +-- end +-- local getter_template = +-- [[ +-- return function(t) +-- local success, value = pcall(function (t) +-- return t%s +-- end, t) +-- if success then +-- return value +-- end +-- end +-- ]] +-- local setter_template = +-- [[ +-- return function(t, v) +-- local success, err = pcall(function (t, v) +-- t%s = v +-- end, t, v) +-- end +-- ]] +-- local getter_cache = {} +-- local setter_cache = {} +-- local function compile_normalized_property(field_path) +-- local getter, setter = getter_cache[field_path], setter_cache[field_path] +-- if not getter then +-- getter = assert(load(getter_template:format(field_path)))() +-- getter_cache[field_path] = getter +-- setter = assert(load(setter_template:format(field_path)))() +-- setter_cache[field_path] = setter +-- end +-- return getter, setter +-- end +-- function _M.compile_property(field_path) +-- return compile_normalized_property(normalize_field_path(field_path)) +-- end +-- local set +-- local function create() +-- local o = { +-- dirty = false, +-- ignored = false, +-- opaque = false, +-- _stage = {}, +-- _traced = {}, +-- _lastversion = {}, +-- _parent = false +-- } +-- return setmetatable( +-- o, +-- { +-- __index = o._stage, +-- __newindex = set, +-- __len = _M.len, +-- __pairs = _M.pairs, +-- __ipairs = _M.ipairs +-- } +-- ) +-- end +-- local function mark_dirty(t) +-- while t and not t.dirty do +-- t.dirty = true +-- t = t._parent +-- end +-- end +-- set = function(t, k, v, force) +-- local stage = t._stage +-- local u = stage[k] +-- if _M.is_traceable(v) then +-- if _M.is(u) then +-- for _ in next, u._stage do +-- if v[k] == nil then +-- u[k] = nil +-- end +-- end +-- else +-- u = create() +-- u._parent = t +-- stage[k] = u +-- end +-- merge(u, v) +-- else +-- local traced = t._traced +-- local lastverison = t._lastversion +-- if stage[k] ~= v or force then +-- if not traced[k] then +-- traced[k] = true +-- lastverison[k] = u +-- end +-- stage[k] = v +-- mark_dirty(t) +-- end +-- end +-- end +-- function _M.is(t) +-- local mt = getmetatable(t) +-- return type(mt) == "table" and mt.__newindex == set +-- end +-- function _M.is_traceable(t) +-- if type(t) ~= "table" then +-- return false +-- end +-- local mt = getmetatable(t) +-- return mt == nil or mt.__newindex == set +-- end +-- function _M.new(t) +-- local o = create() +-- if t then +-- merge(o, t) +-- end +-- return o +-- end +-- function _M.mark_changed(t, k) +-- local v = t[k] +-- if _M.is(v) then +-- mark_dirty(v) +-- else +-- set(t, k, v, true) +-- end +-- end +-- function _M.set_ignored(t, ignored) +-- t.ignored = ignored +-- if not ignored and t.dirty then +-- mark_dirty(t._parent) +-- end +-- end +-- function _M.set_opaque(t, opaque) +-- t.opaque = opaque +-- end +-- local k_stub = +-- setmetatable( +-- {}, +-- { +-- __tostring = function() +-- return "k_stub" +-- end +-- } +-- ) +-- function _M.compile_map(src) +-- local o = {} +-- for i = 1, #src do +-- local v = src[i] +-- local argc = #v +-- -- v = {argc, action, arg1, arg2, ..., argn} +-- v = {argc, unpack(v)} -- copy +-- for j = 1, argc - 1 do +-- local k = j + 2 +-- local arg = normalize_field_path(v[k]) +-- v[k] = compile_normalized_property(arg) +-- local p, c = nil, o +-- for field in arg:gmatch('([^%[%]\'"]+)') do +-- field = tonumber(field) or field +-- p, c = c, c[field] +-- if not c then +-- c = {} +-- p[field] = c +-- end +-- end +-- local stub = c[k_stub] +-- if not stub then +-- stub = {[1] = 1} +-- c[k_stub] = stub +-- end +-- local n = stub[1] + 1 +-- stub[1] = n +-- stub[n] = v +-- end +-- end +-- return o +-- end +-- local function get_map_stub(map, k) +-- local map_k = map[k] +-- return map_k and map_k[k_stub] +-- end +-- local argv = {} +-- local function do_map(t, mappings, mapped) +-- for i = 2, mappings[1] do +-- local mapping = mappings[i] +-- if not mapped[mapping] then +-- mapped[mapping] = true +-- local argc = mapping[1] +-- argv[1] = t +-- for i = 2, argc do +-- argv[i] = mapping[i + 1](t) +-- end +-- local action = mapping[2] +-- action(unpack(argv, 1, argc)) +-- end +-- end +-- end +-- local function next_traced(t, k) +-- local k = next(t._traced, k) +-- return k, t._stage[k], t._lastversion[k] +-- end +-- local function _commit(keep_dirty, t, sub, changed, newestversion, lastversion, map, mapped) +-- if not sub.dirty then +-- return false +-- end +-- local traced = sub._traced +-- local trace = sub._lastversion +-- local has_changed = next(traced) ~= nil +-- for k, v in next, sub._stage do +-- if _M.is(v) and not v.ignored then +-- if v.opaque then +-- has_changed = _commit(keep_dirty, t, v) +-- if has_changed then +-- if changed then +-- changed[k] = true +-- end +-- end +-- else +-- local c, n, l = changed and {}, newestversion and {}, lastversion and {} +-- has_changed = _commit(keep_dirty, t, v, c, n, l, map and map[k], mapped) +-- if has_changed then +-- if changed then +-- changed[k] = c +-- end +-- if newestversion then +-- newestversion[k] = n +-- end +-- if lastversion then +-- lastversion[k] = l +-- end +-- end +-- end +-- if has_changed and map then +-- local stub = get_map_stub(map, k) +-- if stub then +-- do_map(t, stub, mapped) +-- end +-- end +-- end +-- end +-- for k, v, u in next_traced, sub do +-- if not keep_dirty then +-- traced[k] = nil +-- trace[k] = nil +-- end +-- if changed then +-- changed[k] = true +-- end +-- if newestversion then +-- newestversion[k] = v +-- end +-- if lastversion then +-- lastversion[k] = u +-- end +-- if map then +-- local stub = get_map_stub(map, k) +-- if stub then +-- do_map(t, stub, mapped) +-- end +-- end +-- end +-- if not keep_dirty then +-- sub.dirty = false +-- end +-- return has_changed +-- end +-- local flag_map = { +-- [("k"):byte()] = "changed", +-- [("v"):byte()] = "newestversion", +-- [("u"):byte()] = "lastversion" +-- } +-- local function commit(t, map, diff_flags, keep_dirty) +-- local has_changed, diff +-- if diff_flags then +-- diff = {} +-- for i = 1, #diff_flags do +-- local field_name = flag_map[diff_flags:byte(i)] +-- if field_name then +-- diff[field_name] = {} +-- end +-- end +-- has_changed = _commit(keep_dirty, t, t, diff.changed, diff.newestversion, diff.lastversion, map, map and {}) +-- else +-- has_changed = _commit(keep_dirty, t, t, nil, nil, nil, map, map and {}) +-- end +-- return has_changed, diff +-- end +-- function _M.commit(t, map, diff_flags) +-- if type(map) == "string" then +-- map, diff_flags = nil, map +-- end +-- return commit(t, map, diff_flags, false) +-- end +-- function _M.diff(t, map, diff_flags) +-- if type(map) == "string" then +-- map, diff_flags = nil, map +-- end +-- return commit(t, map, diff_flags, true) +-- end +-- local function do_maps(t, sub, map, mapped) +-- for k, map_k in next, map do +-- local stub = map_k[k_stub] +-- if stub then +-- do_map(t, stub, mapped) +-- end +-- local v = sub[k] +-- if type(v) == "table" then +-- do_maps(t, v, map_k, mapped) +-- end +-- end +-- end +-- function _M.map(t, map) +-- do_maps(t, t, map, map and {}) +-- end +-- local function forward_to(field, func) +-- return function(t, ...) +-- return func(t[field], ...) +-- end +-- end +-- function _M.len(t) +-- return #(t._stage) +-- end +-- _M.next = forward_to("_stage", next) +-- _M.pairs = forward_to("_stage", pairs) +-- _M.ipairs = forward_to("_stage", ipairs) +-- _M.unpack = forward_to("_stage", unpack) +-- return _M +-- -- local traceable = require "traceable" +-- -- local mt = { +-- -- __tostring = function(t) +-- -- return "object" +-- -- end +-- -- } +-- -- local object = setmetatable({}, mt) +-- -- local raw_data = { +-- -- boolean_value = false, +-- -- number_value = 1, +-- -- string_value = "string", +-- -- list_value = {1, 2, 3}, +-- -- nested_table_value = { +-- -- nested_table_value = { +-- -- stub_value = true +-- -- } +-- -- }, +-- -- object_value = object +-- -- } +-- -- local data = traceable.new(raw_data) +-- -- data.list_value = {1, 2, 3} +-- -- data.list_value = {1, 2, 3, 4} +-- -- data.list_value = {1, 2, 3} +-- -- traceable.commit(data) +-- -- print("traceable data.dirty 00000 ", data.dirty) +-- -- table.insert(data.list_value, 4) +-- -- traceable.commit(data) +-- -- print("traceable data.dirty aaaa", data.dirty) +-- -- traceable.commit(data) +-- -- print("traceable data.dirty bbbb", data.dirty) +-- -- table.insert(data.list_value, 5) +-- -- table.remove(data.list_value, #data.list_value) +-- -- print("traceable data.dirty ccccc", data.dirty) + + + +local string_find = string.find +local string_match = string.match +local string_format = string.format +local string_sub = string.sub +local table_insert = table.insert +local table_remove = table.remove +local ipairs = ipairs +local tonumber = tonumber +local tostring = tostring +local type = type +local setmetatable = setmetatable +local getmetatable = getmetatable + +local string_split = function(input, delimiter) + input = tostring(input) + delimiter = tostring(delimiter) + if (delimiter == '') then + return false + end + local pos, arr = 0, {} + -- for each divider found + for st, sp in function() + return string_find(input, delimiter, pos, true) + end do + table_insert(arr, string_sub(input, pos, st - 1)) + pos = sp + 1 + end + table_insert(arr, string_sub(input, pos)) + return arr +end + +local table_removebyvalue = function(array, value, removeall) + local c, i, max = 0, 1, #array + while i <= max do + if array[i] == value then + table_remove(array, i) + c = c + 1 + i = i - 1 + max = max - 1 + if not removeall then + break + end + end + i = i + 1 + end + return c +end + +local function getByPath(t, path) + if not path then + return t + elseif string_find(path, "%.") then + local paths = string_split(path, ".") + local cur = t + local hasN = string_find(path, "%[") ~= nil + for i, keyname in ipairs(paths) do + local v + if hasN then + local n = tonumber(string_match(keyname, "^%[(.+)%]$")) + if n then + v = cur[n] + end + end + if not v then + v = cur[keyname] + end + if v then + cur = v + else + return nil + end + end + return cur + else + return t[path] + end +end + +local function setByPath(t, path, value) + if not path then + t = value + elseif string_find(path, "%.") then + local paths = string_split(path, ".") + local cur = t + local count = #paths + local hasN = string_find(path, "%[") ~= nil + for i = 1, count - 1 do + local keyname = paths[i] + local v + if hasN then + local n = tonumber(string_match(keyname, "^%[(.+)%]$")) + if n then + v = cur[n] + keyname = n + end + end + if not v then + v = cur[keyname] + end + if v then + cur = v + else + cur[keyname] = {} + cur = cur[keyname] + end + end + local lastKeyname = paths[count] + if hasN then + lastKeyname = tonumber(string_match(lastKeyname, "^%[(.+)%]$")) or lastKeyname + end + cur[lastKeyname] = value + else + t[path] = value + end + return t +end + + +local table_monitor_ + +function table_monitor_(tb, callback, path) + local data_ = tb or {} + local monitors_ = {} + local spath = path + local function createKey(subpath, key) + if subpath then + if "number" == type(key) then + return string_format("%s.[%s]", subpath, key) + else + return string_format("%s.%s", subpath, key) + end + else + if "number" == type(key) then + return string_format("[%s]", key) + else + return key + end + end + end + local mt = { + __index = function(_, k) + if "__ismonitor__" == k then + return true + end + if not data_ then + return + end + local result = data_[k] + if type(result) == "table" then + if not monitors_[k] then + monitors_[k] = table_monitor_(result, callback, createKey(spath, k)) + end + return monitors_[k] + else + return result + end + end, + __newindex = function(_, k, v) + local oldValue + if data_ then + oldValue = data_[k] + end + if oldValue ~= v then + data_[k] = v + if callback then + local key = createKey(spath, k) + if type(v) == "table" then + monitors_[k] = table_monitor_(v, callback, key) + callback(key, monitors_[k], oldValue) + else + if monitors_[k] then + monitors_[k] = nil + end + callback(key, v, oldValue) + end + end + end + end, + __call = function(t, k, v) + if "[path]" == k then + return spath + elseif "string" == type(k) then + if v then + setByPath(t, k, v) + else + return getByPath(t, k) + end + else + return data_ + end + end, + } + return setmetatable({}, mt) +end + +local M = {} +function M.new(tb, callback) + if tb.__ismonitor__ then + tb = tb() + end + local callbacks = {} + if callback then + callbacks[#callbacks + 1] = callback + end + local addCallback = function(self, cb) + for _, cc in ipairs(callbacks) do + if cc == cb then + return + end + end + callbacks[#callbacks + 1] = cb + end + local removeCallback = function(self, cb) + table_removebyvalue(callbacks, cb, true) + end + local removeAllCallbacks = function(self) + callbacks = {} + end + local cb = function(path, value, oldValue) + for _, listener in ipairs(callbacks) do + listener(path, value, oldValue) + end + end + local result = table_monitor_(tb, cb) + + local mt = getmetatable(result) + local index_fn = mt.__index + mt.__index = function(t, k) + if k == "addCallback" then + return addCallback + elseif k == "removeCallback" then + return removeCallback + elseif k == "removeAllCallbacks" then + return removeAllCallbacks + else + return index_fn(t, k) + end + end + setmetatable(result, mt) + return result +end + +return M + + +-- local traceable = require("traceable") +-- local a = { +-- a1 = 1, +-- a2 = { 1, 2, 3}, +-- a3 = { +-- a31 = "a", +-- a32 = { +-- "b" +-- }, +-- }, +-- } + +-- local ta = traceable.new(a, function ( path, value, oldValue ) +-- print(string.format("table change at %s from ", path), oldValue, "to ", value) +-- end) + +-- print("start~~~") +-- print(ta.a3.a32[1]) +-- ta.a3.a32[2] = 2 +-- ta.a3.a32 = {"c"} +-- ta.a3.a32.a = 3 +-- ta.a3.a32 = 33 +-- print("~~~", ta.a3.a32) +-- print(ta.a2[3]) +-- ta.a3.a32 = {33} +-- print(ta.a3.a32[1]) +-- ta.a3.a32 = nil +-- ta.a3.a32 = {5} +-- print(ta.a3.a32) +-- print("over~~~") \ No newline at end of file diff --git a/framework/lualib/thirdparty/try.lua b/framework/lualib/thirdparty/try.lua new file mode 100755 index 0000000..a534833 --- /dev/null +++ b/framework/lualib/thirdparty/try.lua @@ -0,0 +1,71 @@ +--[[ + https://github.com/moteus/lua-try + https://github.com/djfdyuruiry/lua-try-catch-finally +]] +local functionType = "function" +local xpcall = xpcall +local type = type +local error = error +local debug = debug + +--- +-- @param tryBlock any The block of code to execute as the try block. +-- +-- @return table that can be used to chain try/catch/finally blocks. (Call .catch or .finally of the return value) +-- +return function(tryBlock) + local status, err = true, nil + + if type(tryBlock) == functionType then + status, err = xpcall(tryBlock, debug.traceback) + end + + local finally = function(finallyBlock, catchBlockDeclared) + if type(finallyBlock) == functionType then + finallyBlock() + end + + if not catchBlockDeclared and not status then + error(err) + end + end + + local catch = function(catchBlock) + local catchBlockDeclared = type(catchBlock) == functionType + + if not status and catchBlockDeclared then + local ex = err or "unknown error occurred" + catchBlock(ex) + end + + return { + finally = function(finallyBlock) + finally(finallyBlock, catchBlockDeclared) + end, + } + end + + return { + catch = catch, + finally = function(finallyBlock) + finally(finallyBlock, false) + end, + } +end + +-- local try = require "try" + +-- local object + +-- try(function () +-- object = Object() +-- object:doRiskyStuff() +-- end) +-- .catch(function (ex) +-- print(ex) +-- end) +-- .finally(function () +-- if object then +-- object:dispose() +-- end +-- end) diff --git a/framework/lualib/thirdparty/validation/context.lua b/framework/lualib/thirdparty/validation/context.lua new file mode 100755 index 0000000..f8e584b --- /dev/null +++ b/framework/lualib/thirdparty/validation/context.lua @@ -0,0 +1,44 @@ +local table = table +local pairs = pairs + +local context = {} + +function context.merge_properties(context1, context2) + for key, value in pairs(context2) do + if not context1[key] then + context1[key] = value + end + end +end + +function context.new(rule, properties) + local new_context = {} + new_context.rule = rule + new_context.children = {} + new_context.result = true + + context.merge_properties(new_context, properties or {}) + + function new_context:new_child(rule, properties) + local child = context.new(rule, properties) + + context.merge_properties(child, self) + + child.parent = self + table.insert(self.children, child) + + return child + end + + function new_context:merge(properties) + context.merge_properties(self, properties) + end + + function new_context:apply_rule() + self.rule.apply(self) + end + + return new_context +end + +return context diff --git a/framework/lualib/thirdparty/validation/init.lua b/framework/lualib/thirdparty/validation/init.lua new file mode 100755 index 0000000..90a88cc --- /dev/null +++ b/framework/lualib/thirdparty/validation/init.lua @@ -0,0 +1,111 @@ +local pairs = pairs +local error = error +local setmetatable = setmetatable + +local all = require("validation.rules.all") +local context = require("validation.context") +local message = require("validation.message") + +local default_properties = {} +local last_messager = nil +local messager = error +local validation = {} + +function validation.set_messager(callback) + last_messager = messager + messager = callback +end + +function validation.restore_messager(_) + messager = last_messager or error + last_messager = nil +end + +function validation.set_defaults(properties) + for key, value in pairs(properties) do + default_properties[key] = value + end +end + +function validation:assert(input, properties) + local chain_context + + chain_context = context.new(self, { + input = input, + }) + chain_context:merge(properties or {}) + chain_context:merge(default_properties) + chain_context:apply_rule() + + if not chain_context.result then + local context_message = message.new(chain_context) + + messager(context_message.get_full()) + end +end + +function validation:check(input, properties) + local chain_context + + chain_context = context.new(self, { + input = input, + }) + chain_context:merge(properties or {}) + chain_context:merge(default_properties) + + for _, rule in pairs(self.get_rules()) do + local child_context = chain_context:new_child(rule) + child_context:apply_rule() + + if not child_context.result then + local child_message = message.new(child_context) + + messager(child_message.get_single()) + break + end + end +end + +function validation:validate(input, properties) + local chain_context + + chain_context = context.new(self, { + input = input, + }) + chain_context:merge(properties or {}) + chain_context:merge(default_properties) + chain_context:apply_rule() + + return chain_context.result +end + +function validation.new() + local new_validation = all() + local metatable = {} + + for key, value in pairs(validation) do + new_validation[key] = value + end + + function metatable:__index(key) + self._last = require("validation.rules." .. key) -- Use pcall to other prefices + + return self + end + + function metatable:__call(...) + self:add_rule(self._last(...)) + + return self + end + + return setmetatable(new_validation, metatable) +end + +return setmetatable(validation, { + __index = function(self, key) + local new_validation = validation.new() + + return new_validation[key] + end, +}) diff --git a/framework/lualib/thirdparty/validation/message.lua b/framework/lualib/thirdparty/validation/message.lua new file mode 100755 index 0000000..d26f223 --- /dev/null +++ b/framework/lualib/thirdparty/validation/message.lua @@ -0,0 +1,88 @@ +local string = string +local table = table +local pairs = pairs + + +local message = {} + +function message.build(context) + local messages = context.rule.messages + local mode = context.mode or "affirmative" + local template = context.template or "standard" + local parent = context.parent + local message = messages[mode][template] + + if + context.message and + (not parent or (parent and not parent._message) or + (parent and parent._message and parent.message ~= context.message)) + then + message = context.message + end + + context.placeholder = context.placeholder or context.name or tostring(context.input) + + if context.translator then + message = context.translator(message) + end + + for property in string.gmatch(message, "{{(%a+)}}") do + message = string.gsub(message, "{{" .. property .. "}}", '"' .. tostring(context[property]) .. '"') + end + + context._message = message + + return message +end + +function message.get_messages(context, level) + local messages = {} + + while (#context.children == 1 and level > 1) do + context = context.children[1] + end + + if (context.mode == "negative" or context.result == false) then + table.insert(messages, {level = level, content = message.build(context)}) + end + + for index = 1, #context.children do + local childConstraint = context.children[index] + local childLevel = (level + 1) + local childMessages = message.get_messages(childConstraint, childLevel) + for _, message in pairs(childMessages) do + table.insert(messages, message) + end + end + + return messages +end + +function message.new(context) + return { + get_full = function() + local messages = {} + for _, message in pairs(message.get_messages(context, 1)) do + local prefix = "" + + if message.level > 1 then + prefix = string.rep(" ", (message.level - 1)) .. "- " + end + + table.insert(messages, prefix .. message.content) + end + + return table.concat(messages, "\n") + end, + get_single = function() + local current = context + while #current.children == 1 do + current = current.children[1] + end + + return message.build(current) + end + } +end + +return message diff --git a/framework/lualib/thirdparty/validation/rules/absent.lua b/framework/lualib/thirdparty/validation/rules/absent.lua new file mode 100755 index 0000000..8f06a4b --- /dev/null +++ b/framework/lualib/thirdparty/validation/rules/absent.lua @@ -0,0 +1,15 @@ +return function() + return { + messages = { + affirmative = { + standard = "{{placeholder}} must be defined" + }, + negative = { + standard = "{{placeholder}} cannot be defined" + } + }, + apply = function(context) + context.result = context.input == nil + end + } +end diff --git a/framework/lualib/thirdparty/validation/rules/all.lua b/framework/lualib/thirdparty/validation/rules/all.lua new file mode 100755 index 0000000..4aa7867 --- /dev/null +++ b/framework/lualib/thirdparty/validation/rules/all.lua @@ -0,0 +1,48 @@ +local table = table +local pairs = pairs + +return function(...) + local rules = {...} + return { + messages = { + affirmative = { + standard = "Some rules must pass for {{placeholder}}", + all = "All rules must pass for {{placeholder}}" + }, + negative = { + standard = "Some rules cannot pass for {{placeholder}}", + all = "All rules cannot pass for {{placeholder}}" + } + }, + add_rule = function(self, rule) + table.insert(rules, rule) + return self + end, + get_rules = function() + return rules + end, + apply = function(context) + local failures = 0 + + context.result = true + + if #rules == 0 then + return + end + + for _, rule in pairs(rules) do + local child_context = context:new_child(rule) + child_context:apply_rule() + + if child_context.result == false then + failures = (failures + 1) + context.result = false + end + end + + if failures == #rules then + context.template = "all" + end + end + } +end diff --git a/framework/lualib/thirdparty/validation/rules/between.lua b/framework/lualib/thirdparty/validation/rules/between.lua new file mode 100755 index 0000000..04c08ef --- /dev/null +++ b/framework/lualib/thirdparty/validation/rules/between.lua @@ -0,0 +1,30 @@ +local tonumber = tonumber + +return function(minimum, maximum) + return { + messages = { + affirmative = { + standard = "{{placeholder}} must be between {{minimum}} and {{maximum}}", + number = "{{placeholder}} must be a number to be compared to {{minimum}} and {{maximum}}" + }, + negative = { + standard = "{{placeholder}} cannot be between {{minimum}} and {{maximum}}", + number = "{{placeholder}} cannot be a number to be compared to {{minimum}} and {{maximum}}" + } + }, + apply = function(context) + context.minimum = minimum + context.maximum = maximum + + local number = tonumber(context.input) + + if not number then + context.result = false + context.template = "number" + return + end + + context.result = (number >= minimum and number <= maximum) + end + } +end diff --git a/framework/lualib/thirdparty/validation/rules/dummy.lua b/framework/lualib/thirdparty/validation/rules/dummy.lua new file mode 100755 index 0000000..f905952 --- /dev/null +++ b/framework/lualib/thirdparty/validation/rules/dummy.lua @@ -0,0 +1,15 @@ +return function(result) + return { + messages = { + affirmative = { + standard = "{{placeholder}} with result {{result}} in affirmative mode" + }, + negative = { + standard = "{{placeholder}} with result {{result}} in negative mode" + } + }, + apply = function(context) + context.result = result + end + } +end diff --git a/framework/lualib/thirdparty/validation/rules/equals.lua b/framework/lualib/thirdparty/validation/rules/equals.lua new file mode 100755 index 0000000..19286ad --- /dev/null +++ b/framework/lualib/thirdparty/validation/rules/equals.lua @@ -0,0 +1,16 @@ +return function(expected) + return { + messages = { + affirmative = { + standard = "{{placeholder}} must be equal to {{expected}}" + }, + negative = { + standard = "{{placeholder}} cannot be equal to {{expected}}" + } + }, + apply = function(context) + context.expected = expected + context.result = context.input == expected + end + } +end diff --git a/framework/lualib/thirdparty/validation/rules/key.lua b/framework/lualib/thirdparty/validation/rules/key.lua new file mode 100755 index 0000000..60864be --- /dev/null +++ b/framework/lualib/thirdparty/validation/rules/key.lua @@ -0,0 +1,36 @@ +return function(key, rule, mandatory) + if mandatory == nil then + mandatory = true + end + + return { + messages = { + affirmative = { + standard = "{{key}} must be defined" + }, + negative = { + standard = "{{key}} must not be defined" + } + }, + apply = function(context) + context.key = key + + if context.input[key] == nil then + context.result = (mandatory == false) + return + end + + if not rule then + context.result = true + return + end + + local child_context = context:new_child(rule) + child_context.name = key + child_context.input = context.input[key] + child_context:apply_rule() + + context.result = child_context.result + end + } +end diff --git a/framework/lualib/thirdparty/validation/rules/never.lua b/framework/lualib/thirdparty/validation/rules/never.lua new file mode 100755 index 0000000..ad2a413 --- /dev/null +++ b/framework/lualib/thirdparty/validation/rules/never.lua @@ -0,0 +1,19 @@ +return function(rule) + return { + messages = {}, + apply = function(context) + local child_context = context:new_child(rule) + if child_context.mode == "affirmative" then + child_context.mode = "negative" + elseif child_context.mode == "negative" then + child_context.mode = "affirmative" + else + child_context.mode = "negative" + end + child_context:apply_rule() + child_context.result = (child_context.result == false) + + context.result = child_context.result + end + } +end diff --git a/framework/lualib/thirdparty/validation/rules/number.lua b/framework/lualib/thirdparty/validation/rules/number.lua new file mode 100755 index 0000000..40d4ed8 --- /dev/null +++ b/framework/lualib/thirdparty/validation/rules/number.lua @@ -0,0 +1,17 @@ +local type = type + +return function() + return { + messages = { + affirmative = { + standard = "{{placeholder}} must be a number" + }, + negative = { + standard = "{{placeholder}} cannot be a number" + } + }, + apply = function(context) + context.result = type(context.input) == "number" + end + } +end diff --git a/framework/lualib/thirdparty/validation/rules/numeric.lua b/framework/lualib/thirdparty/validation/rules/numeric.lua new file mode 100755 index 0000000..a55453c --- /dev/null +++ b/framework/lualib/thirdparty/validation/rules/numeric.lua @@ -0,0 +1,17 @@ +local tonumber = tonumber + +return function() + return { + messages = { + affirmative = { + standard = "{{placeholder}} must be numeric" + }, + negative = { + standard = "{{placeholder}} cannot be numeric" + } + }, + apply = function(context) + context.result = (tonumber(context.input) ~= nil) + end + } +end diff --git a/framework/lualib/thirdparty/validation/rules/positive.lua b/framework/lualib/thirdparty/validation/rules/positive.lua new file mode 100755 index 0000000..f8dd3b3 --- /dev/null +++ b/framework/lualib/thirdparty/validation/rules/positive.lua @@ -0,0 +1,23 @@ +local tonumber = tonumber + +return function() + return { + messages = { + affirmative = { + standard = "{{placeholder}} must be positive" + }, + negative = { + standard = "{{placeholder}} cannot be positive" + } + }, + apply = function(context) + local number = tonumber(context.input) + if not number then + context.result = false + return + end + + context.result = (number > 0) + end + } +end diff --git a/framework/lualib/thirdparty/validation/rules/string.lua b/framework/lualib/thirdparty/validation/rules/string.lua new file mode 100755 index 0000000..cd28d35 --- /dev/null +++ b/framework/lualib/thirdparty/validation/rules/string.lua @@ -0,0 +1,17 @@ +local type = type + +return function() + return { + messages = { + affirmative = { + standard = "{{placeholder}} must be a string" + }, + negative = { + standard = "{{placeholder}} cannot be a string" + } + }, + apply = function(context) + context.result = type(context.input) == "string" + end + } +end diff --git a/framework/lualib/thirdparty/vararg.lua b/framework/lualib/thirdparty/vararg.lua new file mode 100755 index 0000000..1f0cf20 --- /dev/null +++ b/framework/lualib/thirdparty/vararg.lua @@ -0,0 +1,198 @@ +-- https://github.com/moteus/lua-vararg +local math = math +local table = table + +local error, assert, select = error, assert, select +local max, unpack = math.max, table.unpack or unpack +local setmetatable = setmetatable + +local tinsert2 = function(t, n, i, v) + -- lua 5.2 rise error if index out of range + -- assert(type(t) =='table') + -- assert(type(n) =='number') + -- assert(type(i) =='number') + if i > n then + t[i] = v + return i + end + + for j = n, i, -1 do + t[j + 1] = t[j] + end + t[i] = v + + return n + 1 +end + +local tremove2 = function(t, n, i) + -- lua 5.2 rise error if index out of range + -- assert(type(t) =='table') + -- assert(type(n) =='number') + -- assert(type(i) =='number') + if i > n then + for j = n + 1, i do + t[j] = nil + end + return n + end + + for j = i, n do + t[j] = t[j + 1] + end + + return n - 1 +end + +local function idx(i, n, d) + if i == nil then + if not d then + return error("number expected, got nil", 2) + end + return d + end + if i < 0 then + i = n + i + 1 + end + if i <= 0 then + return error("index out of bounds", 2) + end + return i +end + +local function pack(...) + local n = select("#", ...) + local v = {...} + return function(...) + if (...) == "#" then + return n + else + local argc = select("#", ...) + if argc == 0 then + return unpack(v, 1, n) + else + local i, j = ... + if i == nil then + if j == nil then + j = 0 + end + i = j + 1 + if i > 0 and i <= n then + return i, v[i] + end + else + i = idx(i, n, 1) + j = idx(j, n, i) + return unpack(v, i, j) + end + end + end + end +end + +local function range(i, j, ...) + local n = select("#", ...) + i, j = idx(i, n), idx(j, n) + if i > j then + return + end + return unpack({...}, i, j) +end + +local function remove(i, ...) + local n = select("#", ...) + local t = {...} + i = idx(i, n) + assert(i > 0, "index out of bounds") + if i <= n then + n = tremove2(t, n, i) + end + return unpack(t, 1, n) +end + +local function insert(v, i, ...) + local n = select("#", ...) + local t = {...} + i = idx(i, n) + assert(i > 0, "index out of bounds") + n = tinsert2(t, n, i, v) + return unpack(t, 1, n) +end + +local function replace(v, i, ...) + local n = select("#", ...) + local t = {...} + i = idx(i, n) + assert(i > 0, "index out of bounds") + t[i] = v + n = max(n, i) + return unpack(t, 1, n) +end + +local function append(...) + local n = select("#", ...) + if n <= 1 then + return ... + end + local t = {select(2, ...)} + t[n] = (...) + return unpack(t, 1, n) +end + +local function map(...) + local n = select("#", ...) + assert(n > 0) + local f = ... + local t = {} + for i = 2, n do + t[i - 1] = f((select(i, ...))) + end + return unpack(t, 1, n - 1) +end + +local function packinto(n, t, ...) + local c = select("#", ...) + for i = 1, c do + t[n + i] = select(i, ...) + end + return n + c +end + +local function concat(...) + local n = 0 + local t = {} + for i = 1, select("#", ...) do + local f = select(i, ...) + n = packinto(n, t, f()) + end + return unpack(t, 1, n) +end + +local function count(...) + return select("#", ...) +end + +local function at(i, ...) + local n = select("#", ...) + i = idx(i, n) + if i > n then + return + end + return (select(i, ...)) +end + +return setmetatable({ + pack = pack, + range = range, + insert = insert, + remove = remove, + replace = replace, + append = append, + map = map, + concat = concat, + count = count, + at = at, +}, { + __call = function(_, ...) + return pack(...) + end, +}) diff --git a/framework/lualib/thirdparty/zset/zset.lua b/framework/lualib/thirdparty/zset/zset.lua new file mode 100644 index 0000000..65b0bb1 --- /dev/null +++ b/framework/lualib/thirdparty/zset/zset.lua @@ -0,0 +1,131 @@ +local skiplist = require "skiplist.c" +local mt = {} +mt.__index = mt + +function mt:add(score, member) + local old = self.tbl[member] + if old then + if old == score then + return + end + self.sl:delete(old, member) + end + + self.sl:insert(score, member) + self.tbl[member] = score +end + +function mt:rem(member) + local score = self.tbl[member] + if score then + self.sl:delete(score, member) + self.tbl[member] = nil + end +end + +function mt:count() + return self.sl:get_count() +end + +function mt:_reverse_rank(r) + return self.sl:get_count() - r + 1 +end + +function mt:limit(count, delete_handler) + local total = self.sl:get_count() + if total <= count then + return 0 + end + + local delete_function = function(member) + self.tbl[member] = nil + if delete_handler then + delete_handler(member) + end + end + + return self.sl:delete_by_rank(count + 1, total, delete_function) +end + +function mt:rev_limit(count, delete_handler) + local total = self.sl:get_count() + if total <= count then + return 0 + end + local from = self:_reverse_rank(count + 1) + local to = self:_reverse_rank(total) + + local delete_function = function(member) + self.tbl[member] = nil + if delete_handler then + delete_handler(member) + end + end + + return self.sl:delete_by_rank(from, to, delete_function) +end + +function mt:rev_range(r1, r2) + r1 = self:_reverse_rank(r1) + r2 = self:_reverse_rank(r2) + return self:range(r1, r2) +end + +function mt:range(r1, r2) + if r1 < 1 then + r1 = 1 + end + + if r2 < 1 then + r2 = 1 + end + return self.sl:get_rank_range(r1, r2) +end + +function mt:rev_rank(member) + local r = self:rank(member) + if r then + return self:_reverse_rank(r) + end + return r +end + +function mt:rank(member) + local score = self.tbl[member] + if not score then + return nil + end + return self.sl:get_rank(score, member) +end + +function mt:range_by_score(s1, s2) + return self.sl:get_score_range(s1, s2) +end + +function mt:score(member) + return self.tbl[member] +end + +function mt:member_by_rank(r) + return self.sl:get_member_by_rank(r) +end + +function mt:member_by_rev_rank(r) + r = self:_reverse_rank(r) + if r > 0 then + return self.sl:get_member_by_rank(r) + end +end + +function mt:dump() + self.sl:dump() +end + +local M = {} +function M.new() + local obj = {} + obj.sl = skiplist() + obj.tbl = {} + return setmetatable(obj, mt) +end +return M diff --git a/framework/lualib/zeus/mainloop/mainloop.lua b/framework/lualib/zeus/mainloop/mainloop.lua new file mode 100644 index 0000000..0fa594c --- /dev/null +++ b/framework/lualib/zeus/mainloop/mainloop.lua @@ -0,0 +1,75 @@ +-- https://github.com/ggabriel96/lasso + +local skynet = require "skynet" + +local default_smoothing = 0.75 +local default_simulations = 60 -- 多少帧 + +local MainLoop = class("MainLoop") + +function MainLoop:ctor(simulations_per_second, fps_smoothing) + self.loop_status = { + fps = 0, + time_frame = 0, + time_total_elapsed = 0, + time_total_simulated = 0, + time_simulation_available = 0, + tick = 0, + time_prev = self:get_time(), + time_curr = self:get_time() + } + + -- 1 秒 按 100 做单位(skynet 进度到 10ms 的原因) + self.delta = 100 / (simulations_per_second or default_simulations) + self.fps_smoothing = fps_smoothing or default_smoothing +end + +function MainLoop:get_time() + return skynet.now() +end + +function MainLoop:compute_fps() + self.loop_status.fps = + math.floor(self.loop_status.fps * self.fps_smoothing) + + math.floor((1 / self.loop_status.time_frame) * (1.0 - self.fps_smoothing)) +end + +-- game_logic 需要实现 方法 init simulate render is_done terminate +function MainLoop:run(game_logic) + game_logic:init() + + while not game_logic:is_done() do + self.loop_status.time_curr = self:get_time() + self.loop_status.time_frame = self.loop_status.time_curr - self.loop_status.time_prev + + self.loop_status.time_simulation_available = + self.loop_status.time_simulation_available + self.loop_status.time_frame + + while (self.loop_status.time_simulation_available >= self.delta) do + self.loop_status.tick = self.loop_status.tick + 1 + + -- if self.loop_status.tick % 10 == 0 then + -- skynet.error("MainLoop tick", self.loop_status.tick, "++++", os.time()) + -- end + + game_logic:simulate(self.loop_status, self.delta) + + self.loop_status.time_simulation_available = self.loop_status.time_simulation_available - self.delta + self.loop_status.time_total_simulated = self.loop_status.time_total_simulated + self.delta + end + + self:compute_fps() + + game_logic:render(self.loop_status, self.delta) + + self.loop_status.time_total_elapsed = self.loop_status.time_total_elapsed + self.loop_status.time_frame + self.loop_status.time_prev = self.loop_status.time_curr + + -- 让出 cpu + skynet.sleep(1) + end + + game_logic:terminate() +end + +return MainLoop diff --git a/framework/lualib/zeus/skynet/env.lua b/framework/lualib/zeus/skynet/env.lua new file mode 100644 index 0000000..5f83337 --- /dev/null +++ b/framework/lualib/zeus/skynet/env.lua @@ -0,0 +1,53 @@ +-- +--[[ + 这是 skynet.getenv 和 skynet.setenv 的中间层,方便使用 + + skynet env 中的 value 是字符串,包括 bool 和 number + + env 自动将 "true" 和 "false" 转换成 bool,将 "number" 字符串转成 number +]] +local skynet = require("skynet") + +local cache = {} + +local function convert(value) + if nil == value then + return nil + elseif "false" == value then + return false + elseif "true" == value then + return true + else + local num = tonumber(value) + if num then + return num + else + return value + end + end +end + +local env = {} + +function env.get(key) + assert("string" == type(key)) + local value = cache[key] + if nil == value then + value = convert(skynet.getenv(key)) + cache[key] = value + return value + else + return value + end +end + +function env.set(key, value) + assert("string" == type(key)) + assert(nil ~= value) + assert("false" ~= value) + assert("true" ~= value) + + skynet.setenv(key, value) + cache[key] = convert(value) +end +return env diff --git a/framework/lualib/zeus/skynet/parallels.lua b/framework/lualib/zeus/skynet/parallels.lua new file mode 100644 index 0000000..d1c62bd --- /dev/null +++ b/framework/lualib/zeus/skynet/parallels.lua @@ -0,0 +1,99 @@ +local skynet = require("skynet") +local xpcall = xpcall +local coroutine = coroutine +local tostring = tostring +local error = error +local next = next +local setmetatable = setmetatable +local assert = assert +local gLog = gLog + +--[[ + 处理 连续多个服务调用时完全独立的,不必等到service1 返回后才调用service2 ... 操作 + 不过 最新 skynet 已经 有 skynet.request { ... } : select(time) 还没有测试使用 + 所以 暂时 先 保留 + + local pa = require("parallels")() + for _, uid in ipairs(uidOrUids) do + pa:add( + function() + data[uid] = self:call("getLordStatsDataCmdRsp", statsID, tag, uid) + end + ) + end + + pa:wait() +]] +local M = {} + +local mt = { + __index = M, +} + +local function new() + return setmetatable({ + list = {}, + boot_co = nil, + boot_error = nil, + }, mt) +end + +function M:wakeup_waitco(err) + if not self.boot_error then + self.boot_error = err + end + local boot_co = self.boot_co + if boot_co then + self.boot_co = nil + -- 唤醒一个被 skynet.sleep 或 skynet.wait 挂起的 coroutine + skynet.wakeup(boot_co) + end +end + +-- 打印异常 +local function exception(e) + gLog.error("[parallel exception]%s", e) + return e +end + +---@param self parallels +---@param func fun(...: any): any... +---@param ... any +---@return void +function M:add(func, ...) + local token = {} + local list = self.list + list[token] = true + -- skynet.fork(func, ...) 从功能上,它等价于 skynet.timeout(0, function() func(...) end) + -- 但是比 timeout 高效一点。因为它并不需要向框架注册一个定时器 + skynet.fork(function(...) + token.co = coroutine.running() + local ok, err = xpcall(func, exception, ...) + if not ok then + self:wakeup_waitco(err) + else + list[token] = nil + if not next(list) then + self:wakeup_waitco() + end + end + end, ...) +end + +function M:wait() + assert(not self.boot_co, "already in wait %s", tostring(self.boot_co)) + self.boot_co = coroutine.running() + if not next(self.list) then + -- skynet.yield() 相当于 skynet.sleep(0) 交出当前服务对 CPU 的控制权 + -- 通常在你想做大量的操作,又没有机会调用阻塞 API 时,可以选择调用 yield 让系统跑的更平滑 + skynet.yield() + else + -- 把当前 coroutine 挂起,之后由 skynet.wakeup 唤醒。token 必须是唯一的,默认为 coroutine.running() + skynet.wait(self.boot_co) + end + if self.boot_error then + error(self.boot_error) + end +end + +return new diff --git a/framework/lualib/zeus/skynet/skynetimer.lua b/framework/lualib/zeus/skynet/skynetimer.lua new file mode 100755 index 0000000..373a0b0 --- /dev/null +++ b/framework/lualib/zeus/skynet/skynetimer.lua @@ -0,0 +1,164 @@ +local skynet = require "skynet" +local shiftimer = require "shiftimer.c" +local assert = assert +local type = type +local ipairs = ipairs +local setmetatable = setmetatable + +--- 状态 +local state = { + INIT = 0, + RUNNING = 1, + PAUSE = 2, + STOP = 3, +} + +local mt = {} +mt.__index = mt + +--- 添加计时 +-- @param ti number 单位ti时间,默认为1/100秒 +-- @param func function 回调函数 +-- @param args any 回调函数的参数 +-- @return boolean true/false +function mt:add(ti, func, args) + assert(type(func) == "function", "timeout callback must be function") + if ti < 0 then + skynet.fork(func, args) + return self.st:nextid() + end + local tid, node = self.st:add(ti) + if tid and not self.tbl[tid] then + self.tbl[tid] = { + func = func, + args = args, + node = node, + } + return tid + end + return false +end + +--- 启动计时器 +-- @return nil +function mt:start() + if self.state == state.INIT then + self.state = state.RUNNING + self.co = skynet.fork(self.__update, self) + end +end + +--- 暂停计时器 +-- @return nil +function mt:pause() + if self.co and self.state ~= state.PAUSE then + self.state = state.PAUSE + end +end + +--- 恢复 +-- @return nil +function mt:resume() + if self.co and self.state == state.PAUSE then + self.state = state.RUNNING + skynet.wakeup(self.co) + end +end + +--- 停止计时器 +-- @return nil +function mt:stop() + if self.co and self.state ~= state.STOP then + self.state = state.STOP + self.co = nil + end +end + +--- 删除计时 +-- @param tid number, 计时器ID +-- @return boolean true/false +function mt:delete(tid) + local tinfo = self.tbl[tid] + if tinfo then + local ok = self.st:del(tinfo.node) + if ok then + self.tbl[tid] = nil + end + return ok + end + return false +end + +--- 设置计时 +-- @param tid number, 计时器ID +-- @param newti number , 新的ti时间,需要重新计算过,底层不负责重新计算ti时间 +-- @return boolean true/false +function mt:reset(tid, newti) + local tinfo = self.tbl[tid] + if tinfo then + local ok = self.st:del(tinfo.node) + if ok then + local func = tinfo.func + local args = tinfo.args + self.tbl[tid] = nil + if newti < 0 then + skynet.fork(func, args) + return self.st:nextid() + end + ok = false + local newtid, node = self.st:add(newti) + if newtid and not self.tbl[newtid] then + self.tbl[newtid] = { + func = tinfo.func, + args = tinfo.args, + node = node, + } + return newtid + end + end + return ok + end + return false +end + +--- 计时器是否正在运行 +-- @return boolean true/false +function mt:isrun() + return self.state == state.RUNNING +end + +--- 每帧调用 +-- @return nil +function mt:__update() + while true do + if self.state == state.RUNNING then + local tids = self.st:update() + if tids then + for _, tid in ipairs(tids) do + local tinfo = self.tbl[tid] + if tinfo and tinfo.func then + skynet.fork(tinfo.func, tinfo.args) + end + self.tbl[tid] = nil + end + end + skynet.sleep(1) + elseif self.state == state.STOP then + break + else + skynet.wait(self.co) + end + end + self.state = state.INIT + -- print("timer update running=", self.state) +end + +local M = {} +function M.new() + local obj = {} + obj.st = shiftimer() + obj.tbl = {} + obj.state = state.INIT + return setmetatable(obj, mt) +end +return M diff --git a/framework/lualib/zeus/skynet/timeout_call.lua b/framework/lualib/zeus/skynet/timeout_call.lua new file mode 100644 index 0000000..5b2fa9f --- /dev/null +++ b/framework/lualib/zeus/skynet/timeout_call.lua @@ -0,0 +1,33 @@ +local skynet = require("skynet") + +local coroutine_running = coroutine.running +local table_pack = table.pack +local table_unpack = table.unpack +local pcall = pcall +local error = error + +-- 注意,这个接口一定会挂起 +return function(ti, f, ...) + local co = coroutine_running() + local ret + + skynet.fork(function(...) + ret = table_pack(pcall(f, ...)) + if co then + skynet.wakeup(co) + end + end, ...) + + skynet.sleep(ti) + co = nil -- prevent wakeup after call + if ret then + if ret[1] then + return table_unpack(ret, 1, ret.n) + else + error(ret[2]) + end + else + -- timeout + return false + end +end diff --git a/framework/lualib/zeus/swt/debug.lua b/framework/lualib/zeus/swt/debug.lua new file mode 100644 index 0000000..59d4330 --- /dev/null +++ b/framework/lualib/zeus/swt/debug.lua @@ -0,0 +1,68 @@ +local skynet = require "skynet" +local debug = require "skynet.debug" +local inject = require "skynet.inject" +---------------------------------------------------------------- + +local function debug_run(source, filename, ...) + local args = table.pack(...) + local ok, output = inject(skynet, source, filename, args, skynet.dispatch_message, skynet.register_protocol) + skynet.ret(skynet.pack(ok, table.concat(output, "\n"))) +end +debug.reg_debugcmd("SWT_RUN", debug_run) + +local function debug_snapshot(len, ti) + collectgarbage("collect") + + local lss = require "lsnapshot" + local json = require "json" + lss.start_snapshot() + + if ti then + ti = tonumber(ti) + end + ti = ti or 10 + skynet.sleep(ti * 100) + + if len then + len = tonumber(len) + end + local rlen, diff = lss.dstop_snapshot(len) + local ret = { + ti = ti, + rlen = rlen, + len = len, + diff = diff, + } + + local filename = "zlogs/snapshot-" .. string.format("%08x", skynet.self()) .. "-" .. os.time() .. ".json" + local file = assert(io.open(filename, "a")) + local jsonstr = json.encode(ret) + file:write(jsonstr) + io.close(file) + + ret.diff = nil + ret.filename = filename + skynet.ret(skynet.pack(ret)) +end + +debug.reg_debugcmd("SWT_SNAPSHOT", debug_snapshot) + +local hotfix_helper = require("hotfix.helper.hotfix_helper") +-- 热更新模块初始化 +hotfix_helper.init() + +-- hotfix_module_names = { "attribute.test_fight_attr" } +-- dbgcmd 00000025 SWT_HOTFIX attribute.test_fight_attr +local function debug_hotfix(module_name) + hotfix_helper.add(module_name) + skynet.ret(skynet.pack({})) +end + +debug.reg_debugcmd("SWT_HOTFIX", debug_hotfix) + +skynet.fork(function() + while true do + hotfix_helper.check() + skynet.sleep(500) + end +end) diff --git a/framework/lualib/zeus/swt/http_helper.lua b/framework/lualib/zeus/swt/http_helper.lua new file mode 100644 index 0000000..c60a798 --- /dev/null +++ b/framework/lualib/zeus/swt/http_helper.lua @@ -0,0 +1,126 @@ +local skynet = require "skynet" +local socket = require "skynet.socket" +local httpd = require "http.httpd" +local sockethelper = require "http.sockethelper" +local websocket = require "http.websocket" +local urllib = require "http.url" + +local util = require "swt.util" +local json = require "json" +local mimetypes = require "mimetypes" + +local function response(id, code, resp, content_type) + if type(resp) == "table" then + resp = json.encode(resp) + content_type = "application/json" + end + + local header = { + -- ["Connection"] = "Keep-Alive", + ["Access-Control-Allow-Origin"] = "*", + ["Access-Control-Allow-Methods"] = "POST,GET,OPTIONS", + ["Access-Control-Allow-Headers"] = "Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Data-Type,X-Requested-With", -- luacheck: ignore + } + if content_type then + header["Content-Type"] = content_type + end + + local ok, err = httpd.write_response(sockethelper.writefunc(id), code, resp, header) + if not ok then + util.log_error("swt http response error fd = %d, %s", id, err) + end + socket.close(id) +end + +local function upgrade(handle_agent, request) + local ok, err = websocket.accept(request.id, handle_agent, "ws", request.addr, { + upgrade = { + header = request.headers, + method = request.method, + url = request.url, + }, + }) + + if not ok and handle_agent.error then + handle_agent.error(request.id, err) + end +end + +local function dispatch(router, id, addr) + local code, url, method, headers, body = httpd.read_request(sockethelper.readfunc(id), 8192) + if not code then + if url ~= sockethelper.socket_error then + util.log_error("swt http read error: %s", url) + end + socket.close(id) + return + end + + if code ~= 200 then + response(id, code) + return + end + + local path, query = urllib.parse(url) + query = urllib.parse_query(query) + + local ok, error = router:execute(method, path, { + id = id, + addr = addr, + path = path, + method = method, + headers = headers, + query = query, + body = body, + }) + + if not ok then + response(id, 404, error) + return + end +end + +local function static(path, options, request) + local real_path = request.path + if options and options.default and real_path == path then + real_path = real_path .. "/" .. options.default + end + + local now = skynet.now() + local cache + local expire = 0 + if options and options.cache then + cache = options.cache[real_path] + expire = options.expire or 30 * 100 + end + + if not cache or cache.expire < now then + cache = { + expire = now + expire, + code = 200, + resp = "", + } + local filename = string.sub(real_path, 2) + local f = io.open(filename, "rb") + if f then + cache.resp = f:read("*a") + cache.content_type = mimetypes.guess(filename or "") + f:close() + else + cache.code = 404 + end + end + + if options and options.cache then + options.cache[real_path] = cache + end + + response(request.id, cache.code, cache.resp, cache.content_type) +end + +return { + response = response, + upgrade = upgrade, + dispatch = dispatch, + static = static, +} diff --git a/framework/lualib/zeus/swt/init.lua b/framework/lualib/zeus/swt/init.lua new file mode 100644 index 0000000..210385a --- /dev/null +++ b/framework/lualib/zeus/swt/init.lua @@ -0,0 +1,32 @@ +local skynet = require "skynet" +local gLog = gLog +----------------------------------------------------------------------------- + +local M = {} + +----------------------------------------------------------------------------- +-- 启动master +-- listen_addr: 管理工具监听端口 例:"0.0.0.0:8000" +function M.start_master(listen_addr) + local master = skynet.uniqueservice("swt/master") + gLog.info("[swt:start_master] listen_addr", listen_addr, " addr ", master) + skynet.send(master, "lua", "start", listen_addr) +end + +----------------------------------------------------------------------------- +-- 启动agent +-- node_type: 节点类型名 +-- node_name: 节点名 +-- listen_addr: 监听地址 例:"127.0.0.1:8001" +function M.start_agent(node_type, node_name, listen_addr) + -- 启动 prometheus + local prometheus_addr = skynet.uniqueservice("swt/prometheus") + + -- 启动 profiler + local agent_addr = skynet.uniqueservice("swt/agent") + gLog.info("[swt:start_agent] node_type node_name listen_addr addr", node_type, node_name, listen_addr, agent_addr, + prometheus_addr) + skynet.send(agent_addr, "lua", "start", node_type, node_name, listen_addr, prometheus_addr) +end + +return M diff --git a/framework/lualib/zeus/swt/profiler.lua b/framework/lualib/zeus/swt/profiler.lua new file mode 100644 index 0000000..c0dfab3 --- /dev/null +++ b/framework/lualib/zeus/swt/profiler.lua @@ -0,0 +1,66 @@ +local c = require "profile.c" + +local M = {} + +-- luacheck: ignore coroutine on_coroutine_destory +local old_co_create = coroutine.create +local old_co_wrap = coroutine.wrap + +function coroutine.create(f) + return old_co_create( + function(...) + c.mark() + return f(...) + end + ) +end + +function coroutine.wrap(f) + return old_co_wrap( + function(...) + c.mark() + return f(...) + end + ) +end + +local exists = 0 +function M.start() + if exists == 0 then + c.start() + end + exists = exists + 1 +end + +function M.sort(tab) + -- 最大的在前面 + table.sort( + tab, + function(v1, v2) + return v1.rettime > v2.rettime + end + ) + + if tab.children and next(tab.children) then + for n = 1, #tab.children do + if tab.children[n] then + tab.children[n] = M.sort(tab.children[n]) + end + end + end + + return tab +end + +function M.stop() + local record_time, nodes = c.dump() + exists = exists - 1 + if exists <= 0 then + exists = 0 + c.stop() + end + + return {time = record_time, nodes = nodes} +end + +return M diff --git a/framework/lualib/zeus/swt/prometheus.lua b/framework/lualib/zeus/swt/prometheus.lua new file mode 100644 index 0000000..ef273e8 --- /dev/null +++ b/framework/lualib/zeus/swt/prometheus.lua @@ -0,0 +1,345 @@ +--- Prometheus client skynet 实现. +-- +-- 所有的 Metrics 应该在服务器启动的时候先全部创建好并创建。 +-- +-- local prometheus = require "prometheus" +-- prometheus.gauge('online_users', '在线玩家数量'):register() +-- prometheus.counter('exceptions_count', '异常数量'):register() +-- prometheus.histogram('request_duration_seconds', {'profile_id', 'request_status'}):register() +-- +-- 在需要记录统计数值的服务中在去创建相同名字的 Metric,注意不要重复注册,会出错,而且只需要传 name 就可以了,其它都不用了 +-- +-- # in agent.lua +-- local prometheus = require "prometheus" +-- local metric_online_users = prometheus.gauge('online_users') +-- +-- local init(...) +-- metric_online_users:inc() +-- ... +-- end +-- +-- local on_exit() +-- metric_online_users:dec() +-- end +-- +-- 如果要在 xpcall 中的 error handler 中使用,因为不能在 error handler 中调用一些 skynet 方法,最好在服务启动的地方先调用一次 `prometheus.default_registry()` + +-- https://github.com/apache/apisix/blob/master/doc/zh-cn/plugins/prometheus.md +-- https://yunlzheng.gitbook.io/prometheus-book/part-ii-prometheus-jin-jie/exporter/what-is-prometheus-exporter +-- https://kalasearch.cn/blog/grafana-with-prometheus-tutorial/ +-- https://www.cnblogs.com/chanshuyi/p/02_grafana_quick_start.html + +-- https://prometheus.io/docs/instrumenting/writing_exporters/ +-- https://github.com/pourer/pika_exporter +-- https://www.jianshu.com/p/9646ce49f722 +-- https://github.com/prometheus/haproxy_exporter +-- https://github.com/yunlzheng/prometheus-book + +-- https://yunlzheng.gitbook.io/prometheus-book/parti-prometheus-ji-chu/promql/prometheus-metrics-types +-- https://frezc.github.io/2019/08/03/prometheus-metrics/ + +-- Prometheus中主要使用的四类指标类型,如下所示 +-- Counter (累加指标) +-- Gauge (测量指标) +-- Summary (概略图) +-- Histogram (直方图) + +-- Counter 一个累加指标数据,这个值随着时间只会逐渐的增加, +-- 比如程序完成的总任务数量,运行错误发生的总次数。 +-- 常见的还有交换机中snmp采集的数据流量也属于该类型, +-- 代表了持续增加的数据包或者传输字节累加值。 + +-- Gauge代表了采集的一个单一数据,这个数据可以增加也可以减少, +-- 比如CPU使用情况,内存使用量,硬盘当前的空间容量等等 + +-- Histogram和Summary使用的频率较少,两种都是基于采样的方式。 +-- 另外有一些库对于这两个指标的使用和支持程度不同,有些仅仅实现了部分功能。 +-- 这两个类型对于某一些业务需求可能比较常见,比如查询单位时间内:总的响应时间低于300ms的占比,或者查询95%用户查询的门限值对应的响应时间是多少。 +-- 使用Histogram和Summary指标的时候同时会产生多组数据,_count代表了采样的总数,_sum则代表采样值的和。 _bucket则代表了落入此范围的数据 + +local skynet = require "skynet" + +local sgsub = string.gsub +local tsort = table.sort +local tinsert = table.insert + +local prometheus = {} + +local INF = math.huge +local DEFAULT_BUCKETS = {0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1.0, 2.5, 5.0, 7.5, 10.0, INF} + +local METRIC_NAME_PATTERN = "[a-zA-Z_:][a-zA-Z0-9_:]*" +local METRIC_LABEL_NAME_PATTERN = "[a-zA-Z_][a-zA-Z0-9_]*" +local RESERVED_METRIC_LABEL_NAME_PATTERN = "__.*" + +local function fully_match(s, pattern) + return sgsub(s, pattern, "") == "" +end + +local function is_valid_name(name) + return fully_match(name, METRIC_NAME_PATTERN) +end + +local function is_valid_label_name(name) + return fully_match(name, METRIC_LABEL_NAME_PATTERN) and not fully_match(name, RESERVED_METRIC_LABEL_NAME_PATTERN) +end + +local DEFAULT_REGISTRIES +local REGISTRY + +--- 获得默认注册服务. +function prometheus.default_registry() + if REGISTRY == nil then + REGISTRY = skynet.uniqueservice("swt/prometheus") + end + return REGISTRY +end +local default_registry = prometheus.default_registry + +--- 获得 Regstiry 所有的数据. +-- @treturn string metrics +function prometheus.collect(registry) + return skynet.call(registry or default_registry(), "lua", "collect") +end + +local function each_registry(collector) + if next(collector._registries) then + return pairs(collector._registries) + else + if DEFAULT_REGISTRIES == nil then + local registry = default_registry() + DEFAULT_REGISTRIES = {[registry] = registry} + end + return pairs(DEFAULT_REGISTRIES) + end +end + +local Collector = {} +Collector.__index = Collector + +local Counter = setmetatable({_type = "counter"}, Collector) +Counter.__index = Counter + +--- 创建一个 counter. +-- +-- Counter 用来计算,比如请求数量,异常数量等等 +-- +-- @tparam string name Metric 名字 +-- @tparam string help Metric 用途说明 +-- @tparam[opt] tab labels 标签名字数组 +-- @treturn Counter +function prometheus.counter(name, help, labels) + return Counter:new(name, help, labels) +end + +local Gauge = setmetatable({_type = "gauge"}, Collector) +Gauge.__index = Gauge + +--- 创建一个 gauge. +-- +-- Gauge 用来记录一个可以自由变化的数值,可以用来记录在线人数等等。 +-- +-- @tparam string name Metric 名字 +-- @tparam string help Metric 用途说明 +-- @tparam[opt] tab labels 标签名字数组 +-- @treturn Gauge +function prometheus.gauge(name, help, labels) + return Gauge:new(name, help, labels) +end + +local Summary = setmetatable({_type = "summary"}, Collector) +Summary.__index = Summary + +--- 创建一个 summary. +-- +-- Summary 同时记录观测值的出现数量和总和。可以用来同时统计请求数量和总用时。 +-- +-- @tparam string name Metric 名字 +-- @tparam string help Metric 用途说明 +-- @tparam[opt] tab labels 标签名字数组 +-- @treturn Gauge +function prometheus.summary(name, help, labels) + return Summary:new(name, help, labels) +end + +local Histogram = setmetatable({_type = "histogram"}, Collector) +Histogram.__index = Histogram + +--- 创建一个 histogram. +-- +-- Histogram 除了 summary 统计的信息,还会根据 buckets 分别统计落入不同区间的观测值的数量。 +-- +-- 每个 Bucket 指定了上限,所有观测值小于或者等于上限的 buckets 都会加 1。 +-- +-- @tparam string name Metric 名字 +-- @tparam string help Metric 用途说明 +-- @tparam[opt] tab labels 标签名字数组 +-- @tparam[opt] tab buckets 每个区间的上限 +-- @treturn Gauge +function prometheus.histogram(name, help, labels, buckets) + return Histogram:new(name, help, labels):buckets(buckets) +end + +--- Counter, Gauge, Summary, Histogram 共享的方法. +-- @type Collector + +function Collector:new(name, help, labels) + assert(name ~= nil and name ~= "", "Require metric name") + assert(is_valid_name(name), "Invalid metric name: " .. name) + labels = labels or {} + return setmetatable( + { + _name = name, + _help = help, + _labels = labels, + _registries = {}, + _type = self._type + }, + self + ) +end + +--- 设置标签名. +-- +-- 只有在调用 register 注册之前设置才有用 +-- +-- @tparam tab labels 标签名数组 +-- @return collector 返回自己 +function Collector:labels(labels) + labels = labels or {} + for _, l in ipairs(labels) do + assert(is_valid_label_name(l), "Invalid label name: " .. l) + end + self._labels = labels + return self +end + +--- 注册 metric. +-- +-- 注册后才会把数据真正记录下来. +-- +-- @param[opt] registry 注册服务,缺省使用默认的注册服务 +-- @return collector 返回自己 +function Collector:register(registry) + assert(self._help ~= nil and self._help ~= "", "Require metric help") + for _, l in ipairs(self._labels) do + assert(is_valid_label_name(l), "Invalid label name: " .. l) + end + registry = registry or default_registry() + if self._registries[registry] == nil then + skynet.call(registry, "lua", "register", self._name, self._help, self._type, self._labels, self._buckets) + self._registries[registry] = registry + end + + return self +end + +--- 取消注册 metric. +-- +-- @param[opt] registry 注册服务,缺省使用默认的注册服务 +-- @return collector 返回自己 +function Collector:unregister(registry) + registry = registry or default_registry() + if self._registries[registry] ~= nil then + skynet.call(registry, "lua", "unregister", self._name) + self._registries[registry] = nil + end + + return self +end + +--- Counter metric. +-- @type Counter + +--- 增长 Counter 的计数. +-- @tparam number num 非负数,可以是浮点数 +-- @tparam tab labels_values 标签的值数组 +function Counter:inc(num, labels_values) + for _, registry in each_registry(self) do + skynet.send(registry, "lua", "submit", self._name, "inc", num or 1, labels_values) + end + return self +end + +--- Gauge metric. +-- @type Gauge + +--- 增加 Gauge 的数量. +-- @tparam number num 变化数量 +-- @tparam tab labels_values 标签的值数组 +function Gauge:inc(num, labels_values) + for _, registry in each_registry(self) do + skynet.send(registry, "lua", "submit", self._name, "inc", num or 1, labels_values) + end + return self +end + +--- 减少 Gauge 的数量. +-- @tparam number num 变化数量 +-- @tparam tab labels_values 标签的值数组 +function Gauge:dec(num, labels_values) + for _, registry in each_registry(self) do + skynet.send(registry, "lua", "submit", self._name, "dec", num or 1, labels_values) + end + return self +end + +--- 将 Gauge 数量设置为指定值. +-- @tparam number num 指定 Gauge 的值 +-- @tparam tab labels_values 标签的值数组 +function Gauge:set(num, labels_values) + for _, registry in each_registry(self) do + skynet.send(registry, "lua", "submit", self._name, "set", num or 1, labels_values) + end + return self +end + +--- Summary metric. +-- @type Summary + +--- 记录一个 Summary 的观察值. +-- @tparam number num 观察值 +-- @tparam tab labels_values 标签的值数组 +function Summary:observe(num, labels_values) + assert(num ~= nil, "Require num") + for _, registry in each_registry(self) do + skynet.send(registry, "lua", "submit", self._name, "observe", num, labels_values) + end + + return self +end + +--- Histogram metric. +-- @type Histogram + +--- 自定义 Histogram 的 buckets. +-- 只有在调用 register 注册之前设置才有用 +-- @tparam tab buckets 每个 buckets 的上限组成的数组. +-- @return histogram 返回自己 +function Histogram:buckets(buckets) + if buckets == nil then + self._buckets = DEFAULT_BUCKETS + else + tsort(buckets) + if buckets[#buckets] ~= INF then + tinsert(buckets, INF) + end + self._buckets = buckets + end + + return self +end + +--- 记录一个 Histogram 的观察值. +-- @tparam number num 观察值 +-- @tparam tab labels_values 标签的值数组 +function Histogram:observe(num, labels_values) + assert(num ~= nil, "Require num") + for _, registry in each_registry(self) do + skynet.send(registry, "lua", "submit", self._name, "observe", num, labels_values) + end + + return self +end + +return prometheus diff --git a/framework/lualib/zeus/swt/util.lua b/framework/lualib/zeus/swt/util.lua new file mode 100644 index 0000000..4a3ee07 --- /dev/null +++ b/framework/lualib/zeus/swt/util.lua @@ -0,0 +1,39 @@ +local json = require "json" +local gLog = gLog +local M = {} + +function M.log_info(...) + gLog.info(string.format(...)) +end + +function M.log_error(...) + gLog.error(string.format(...)) +end + +function M.pack(cmd, data) + return json.encode({ + cmd = cmd, + data = data, + }) +end + +function M.unpack(msg) + local pack = json.decode(msg) + return pack.cmd, pack.data +end + +function M.bind(func, ...) + local rest = {...} + return function(...) + local args = {} + for i = 1, #rest do + table.insert(args, rest[i]) + end + for i = 1, select("#", ...) do + table.insert(args, select(i, ...)) + end + return func(table.unpack(args)) + end +end + +return M diff --git a/framework/lualib/zeus/swt/ws_client.lua b/framework/lualib/zeus/swt/ws_client.lua new file mode 100644 index 0000000..fb15cf0 --- /dev/null +++ b/framework/lualib/zeus/swt/ws_client.lua @@ -0,0 +1,93 @@ +local skynet = require "skynet" +local websocket = require "http.websocket" +local util = require "swt.util" + +local mt = {} +mt.__index = mt + +function mt:connect() + while not (self.conn or self.closed) do + local ok, conn = pcall(websocket.connect, self.addr, nil, 100) + if ok then + self.conn = conn + util.log_info("connect to %s succeed", self.addr) + + if self.options and self.options.on_connected_cb then + self.options.on_connected_cb(self) + end + break + end + + -- util.log_error("connect %s failed: %s", self.addr, conn) + skynet.sleep(100) + end + + return self.conn +end + +function mt:send(cmd, pack) + if self.conn then + return websocket.write(self.conn, util.pack(cmd, pack), "binary") + end +end + +function mt:close() + self:_close() + self.closed = true +end + +function mt:_dispatch(data) + local cmd, msg = util.unpack(data) + if self.options and self.options.handler then + self.options.handler(cmd, msg) + end +end + +function mt:_close() + if not self.conn then + return + end + + self.conn = nil + + websocket.close(self.conn) + if self.options and self.options.on_disconnected_cb then + self.options.on_disconnected_cb(self) + end +end + +function mt:_run() + while true do + if not self:connect() then + break + end + + while self.conn do + local ok, resp, close_reason = pcall(websocket.read, self.conn) + if not ok or not resp then + util.log_error("connection %s read error: %s", self.addr, resp or close_reason) + break + end + + skynet.fork(self._dispatch, self, resp) + end + self:_close() + end +end + +local M = {} + +function M.new(addr, options) + local obj = { + addr = addr, + conn = nil, + closed = false, + options = options, + } + setmetatable(obj, mt) + + skynet.fork(obj._run, obj) + return obj +end + +return M diff --git a/framework/lualib/zeus/tracing/Readme b/framework/lualib/zeus/tracing/Readme new file mode 100644 index 0000000..dc6ecc6 --- /dev/null +++ b/framework/lualib/zeus/tracing/Readme @@ -0,0 +1,16 @@ +源代码库 https://github.com/esrrhs/cLua + + +#clua 编译 +go get "github.com/milochristiansen/lua" +go build clua.go + +#genhtml 工具 +brew install lcov + + +#安装运行 +docker-compose up -d + +#查看 +http://127.0.0.1:16686/search \ No newline at end of file diff --git a/framework/lualib/zeus/tracing/conf.lua b/framework/lualib/zeus/tracing/conf.lua new file mode 100644 index 0000000..7b6b016 --- /dev/null +++ b/framework/lualib/zeus/tracing/conf.lua @@ -0,0 +1,17 @@ +local fmt = string.format +local gEnv = gEnv +local tonumber = tonumber + +------------------------------------------------ + +local ZIPKIN_HOST = "127.0.0.1" +local ZIPKIN_PORT = 9411 + +local node_id = tostring(gEnv.NODEID) +return { + http_host = fmt("%s:%d", ZIPKIN_HOST, ZIPKIN_PORT), + http_uri = "/api/v2/spans", + node_id = tonumber(node_id), -- 节点id + flush_max_span = 10, -- span个数 + flush_interval = 300, -- 间隔时间(秒) +} diff --git a/framework/lualib/zeus/tracing/demo.txt b/framework/lualib/zeus/tracing/demo.txt new file mode 100644 index 0000000..cf1a824 --- /dev/null +++ b/framework/lualib/zeus/tracing/demo.txt @@ -0,0 +1,95 @@ +见 https://zipkin.io/zipkin-api/#/default/post_spans + + +curl -X POST -s localhost:9411/api/v2/spans -H'Content-Type: application/json' -d'[{ + "traceId": "5345234523452243", + "id": "5345234523452243", + "name": "login", + "timestamp": 1612145253678001, + "duration": 1000, + "localEndpoint": { + "serviceName": "chat" + }, + "remoteEndpoint": { + "serviceName": "shop" + }, + "tags": { + "param": "{\"test\":1,\"endter\":2}", + "return": "100" + } +}]' + +curl -X POST -s localhost:9411/api/v2/spans -H'Content-Type: application/json' -d'[{ + "traceId": "5345234523452243", + "id": "5345234523452244", + "parentId": "5345234523452243", + "name": "set", + "timestamp": 1612145353678001, + "duration": 1000, + "localEndpoint": { + "serviceName": "dbdata" + }, + "remoteEndpoint": { + "serviceName": "chat" + }, + "tags": { + "param": "{\"test\":1,\"endter\":2}", + "return": "100" + } +}]' + +curl -X POST -s localhost:9411/api/v2/spans -H'Content-Type: application/json' -d'[{ + "traceId": "5345234523452246", + "id": "5345234523452246", + "name": "set", + "timestamp": 1612406723000000, + "duration": 1000, + "localEndpoint": { + "serviceName": "locker" + }, + "remoteEndpoint": { + "serviceName": "chat" + }, + "tags": { + "param": "{\"test\":1,\"endter\":2}", + "return": 100 + } +}]' + + + +curl -X POST -s localhost:9411/api/v2/spans -H'Content-Type: application/json' -d'[{ + "traceId": "5345234523452243", + "id": "5345234523452246", + "parentId": "5345234523452245", + "name": "set", + "timestamp": 1612145353678001, + "duration": 1000, + "localEndpoint": { + "serviceName": "global" + }, + "tags": { + "param": "{\"test\":1,\"endter\":2}", + "return": "100" + }, + "annotations":[{ + "param": {"name":1,"age":2}, + "return": 100} + ] +}]' + +curl -X POST -s localhost:9411/api/v2/spans -H'Content-Type: application/json' -d'[{ + "traceId": "5345234523452243", + "id": "5345234523452247", + "parentId": "5345234523452245", + "name": "set", + "timestamp": 1612145353678001, + "duration": 1000, + "localEndpoint": { + "serviceName": "dbdata", + }, + "tags": { + "param": "{\"test\":1,\"endter\":2}", + "return": "100" + } +}]' diff --git a/framework/lualib/zeus/tracing/reporter.lua b/framework/lualib/zeus/tracing/reporter.lua new file mode 100644 index 0000000..88e5ed7 --- /dev/null +++ b/framework/lualib/zeus/tracing/reporter.lua @@ -0,0 +1,84 @@ +local skynet = require("skynet") +local httpc = require "http.httpc" +local json = require "json" +local conf = require("tracing.conf") +local assert = assert + +------------------------------------------------ + +local zipkin_reporter_methods = {} +local zipkin_reporter_mt = { + __index = zipkin_reporter_methods, +} + +local function new() + assert(conf.http_host) + assert(conf.http_uri) + + local reporter = setmetatable({ + http_host = conf.http_host, + http_uri = conf.http_uri, + pending_spans = {}, + pending_spans_n = 0, + }, zipkin_reporter_mt) + reporter:init() + return reporter +end + +function zipkin_reporter_methods:init() + skynet.timeout(conf.flush_interval * 100, handler(self, self.flush)) +end + +function zipkin_reporter_methods:add_span(span) + local i = self.pending_spans_n + 1 + self.pending_spans[i] = span + self.pending_spans_n = i + + -- gLog.d("span", i, json.encode(span)) + -- Log.d("add_span", i, json.encode(self.pending_spans)) + + if self.pending_spans_n >= conf.flush_max_span then + self:flush() + end +end + +function zipkin_reporter_methods:timerHander() + self.flush() + skynet.timeout(conf.flush_interval * 100, handler(self, self.flush)) +end + +local header = { + ["content-type"] = "application/json", +} + +function zipkin_reporter_methods:flush() + if self.pending_spans_n == 0 then + return true + end + + if self.http_host == nil then + return true + end + + local pending_spans = json.encode(self.pending_spans) + if not pending_spans then + -- Log.e("pending_spans encode failed", self.pending_spans) + return false + end + + self.pending_spans = {} + self.pending_spans_n = 0 + + local recvheader = {} + local status, body = httpc.request("POST", self.http_host, self.http_uri, recvheader, header, pending_spans) + -- Log.d("zipkin_reporter_methods:flush", pending_spans, status, body) + if status < 200 or status >= 300 then + return false, "invalid response code: " .. status .. " body: " .. body + end + + return true +end + +return { + new = new, +} diff --git a/framework/lualib/zeus/tracing/skynet.lua b/framework/lualib/zeus/tracing/skynet.lua new file mode 100644 index 0000000..59e9454 --- /dev/null +++ b/framework/lualib/zeus/tracing/skynet.lua @@ -0,0 +1,182 @@ +local c = require "skynet.core" +local skynet = require("skynet") +local new_span = require("tracing.span").new +local json = require("json") +local clua = require("clua") +local coroutine = coroutine +local tostring = tostring +local pcall = pcall +local next = next +local pairs = pairs +------------------------------------------------------- + +local skynet_send = skynet.send +local skynet_call = skynet.call +local skynet_dispatch = skynet.dispatch +local skynet_register_protocol = skynet.register_protocol + +skynet.PTYPE_MESSAGE_COLLOCTOR = 13 -- use for msg colloctor + +-- messageColloctor +-- 消息追踪地址 +local message_source_head = {} +local message_collector_head = {} +local message_running_head = {} +local message_self = false + +local hook_thread +local hook_addr = {} +local hook_flag + +local function set_message_source_head(source, trace_id, parent_id) + message_source_head[source] = { + trace_id = trace_id, + parent_id = parent_id, + } +end + +local hook_cmd = {} + +function hook_cmd.send_collect_info(source, trace_id, parent_id) + set_message_source_head(source, trace_id, parent_id) +end + +function hook_cmd.start_hook(_) + if hook_thread == nil then + hook_flag = true + end +end + +function hook_cmd.stop_hook(_) + skynet.stop_hook_collector() +end + +local function getSpan() + local running_thread = tostring(coroutine.running()) + return message_running_head[running_thread] +end + +skynet_register_protocol({ + name = "message_collector", + id = skynet.PTYPE_MESSAGE_COLLOCTOR, + pack = skynet.pack, + unpack = skynet.unpack, + dispatch = function(_, source, cmd, ...) + local f = hook_cmd[cmd] + if f then + f(source, ...) + -- else + -- Log.d(cmd) + end + + end, +}) + +function skynet.send(addr, ...) + local span = getSpan() + if span and not message_self then + skynet.send_collect_info(addr, span) + end + if hook_thread == coroutine.running() then + skynet.send_start_hook(addr) + end + return skynet_send(addr, ...) +end + +function skynet.call(addr, ...) + local span = getSpan() + if span and not message_self then + skynet.send_collect_info(addr, span) + end + + if hook_thread == coroutine.running() then + skynet.send_start_hook(addr) + end + return skynet_call(addr, ...) +end + +function skynet.dispatch(typename, func) + if typename ~= skynet.PTYPE_MESSAGE_COLLOCTOR then + local message_collect_dipatch = function(session, source, cmd, ...) + local running_thread = tostring(coroutine.running()) + local head = message_source_head[source] + if head then + message_source_head[source] = nil + local span = new_span(cmd, head.trace_id, head.parent_id) + message_running_head[running_thread] = span + elseif (cmd and message_collector_head[cmd]) then + local span = new_span(cmd) + message_running_head[running_thread] = span + end + if hook_flag then + skynet.start_hook_collector() + hook_flag = false + end + func(session, source, cmd, ...) + local span = message_running_head[running_thread] + if span then + message_running_head[running_thread] = nil + local param = {...} + if next(param) then + local ok, jsonValue = pcall(json.encode, param) + if ok then + span:set_tag("param", jsonValue) + end + end + span:finish() + end + end + + return skynet_dispatch(typename, message_collect_dipatch) + end +end + +function skynet.start_message_collector(funcName) + message_collector_head[funcName] = true +end + +function skynet.stop_message_collector(funcName) + message_collector_head[funcName] = nil +end + +function skynet.start_hook_collector(filename) + if hook_thread == nil then + -- Log.d("start_hook_collector", SERVICE_NAME) + hook_thread = coroutine.running() + filename = filename or string.format("%s_%d.cov", SERVICE_NAME, os.time()) + clua.start(hook_thread, filename) + end +end + +function skynet.stop_hook_collector() + if hook_thread then + -- Log.d("stop_hook_collector", SERVICE_NAME) + clua.stop(hook_thread) + hook_thread = nil + for addr in pairs(hook_addr) do + skynet.send_trace_msg(addr, "stop_hook") + end + end +end + +function skynet.send_start_hook(addr) + if not hook_addr[addr] then + hook_addr[addr] = true + -- Log.d("skynet.send_start_hook", SERVICE_NAME, coroutine.running(), addr) + skynet.send_trace_msg(addr, "start_hook") + end +end + +function skynet.set_message_self() + message_self = true +end + +function skynet.send_collect_info(addr, span) + skynet.send_trace_msg(addr, "send_collect_info", span.traceId, span.id) +end + +function skynet.send_trace_msg(addr, ...) + c.send(addr, skynet.PTYPE_MESSAGE_COLLOCTOR, 0, skynet.pack(...)) +end + +return skynet diff --git a/framework/lualib/zeus/tracing/span.lua b/framework/lualib/zeus/tracing/span.lua new file mode 100644 index 0000000..ac8ca99 --- /dev/null +++ b/framework/lualib/zeus/tracing/span.lua @@ -0,0 +1,82 @@ +local floor = math.floor +local assert = assert +local type = type +local util = require("tracing.util") +-- local skynet = require("skynet") +local setmetatable = setmetatable +------------------------------------------------- + +local span_methods = {} +local span_mt = { + __index = span_methods, +} + +-- local messageColloctorAddr + +local function new(operat_name, trace_id, parent_id, kind) + kind = kind or "SERVER" + local nowTime = util.get_timestamp() + local span_id = util.generate_span_id(nowTime) + if not trace_id then + trace_id = span_id + end + assert(kind == "SERVER" or kind == "CLIENT", "invalid span kind") + assert(type(operat_name) == "string" and operat_name ~= "", "invalid span name") -- 方法名 + assert(type(trace_id) == "string", "invalid trace id") + assert(type(span_id) == "string", "invalid span id") + if parent_id then + assert(type(parent_id) == "string", "invalid parent id") + end + return setmetatable({ + kind = kind, + traceId = trace_id, + id = span_id, + parentId = parent_id, + name = operat_name, + timestamp = nowTime, + localEndpoint = { + serviceName = SERVICE_NAME, + }, + }, span_mt) +end + +function span_methods:finish() + assert(self.duration == nil, "span already finished") + local duration = util.get_timestamp() - self.timestamp + assert(duration >= 0, "invalid span duration") + self.duration = floor(duration) + -- TODO: xiaojin 增加 前缀 + -- self:set_tag("k", gKingdomID and tostring(gKingdomID)) + -- self:set_tag("m", gMapID and tostring(gMapID)) + -- self:set_tag("u", gUid and tostring(gUid)) + -- self:set_tag("p", tostring(skynet.self())) + + -- if not messageColloctorAddr then + -- messageColloctorAddr = svrAddressMgr.getSvr(svrAddressMgr.messageCollectorService) + -- end + -- skynet.send(messageColloctorAddr, "lua", "sendMessage", TableUtil.cloneWithoutMeta(self)) + return true +end + +function span_methods:set_tag(key, value) + if not value then + return false + end + assert(type(key) == "string", "invalid tag key must string") + assert(type(value) == "string", "invalid tag value must string") + + local tags = self.tags + if tags then + tags[key] = value + else + tags = { + [key] = value, + } + self.tags = tags + end + return true +end + +return { + new = new, +} diff --git a/framework/lualib/zeus/tracing/util.lua b/framework/lualib/zeus/tracing/util.lua new file mode 100644 index 0000000..6853f8a --- /dev/null +++ b/framework/lualib/zeus/tracing/util.lua @@ -0,0 +1,32 @@ +local skynet = require "skynet" +local conf = require("tracing.conf") +local floor = math.floor +local format = string.format +---------------------------------------------- + +local M = {} + +local trace_id = 1 +local last_unixtime = 0 +-- 节点id(8位 255) + 服务地址(12位 4095) + 时间(32位) + 自增id(12位) +local PRE_TRACE_ID = (conf.node_id << 56) + (skynet.self() << 44) +function M.get_timestamp() + return floor(skynet.hpc() / 1000) -- 微妙 +end + +function M.generate_span_id(tm) + tm = tm // 1000 + if last_unixtime ~= tm then + trace_id = 1 + end + local id = (tm << 12) + trace_id + local spanid = format("%016x", PRE_TRACE_ID + id) + + if last_unixtime == tm then + trace_id = trace_id + 1 + end + last_unixtime = tm + return spanid +end + +return M diff --git a/framework/lualib/zeus/zlog/inspect.lua b/framework/lualib/zeus/zlog/inspect.lua new file mode 100644 index 0000000..c88e0e8 --- /dev/null +++ b/framework/lualib/zeus/zlog/inspect.lua @@ -0,0 +1,377 @@ +local inspect = { + _VERSION = "inspect.lua 3.1.0", + _URL = "http://github.com/kikito/inspect.lua", + _DESCRIPTION = "human-readable representations of tables", + _LICENSE = [[ + MIT LICENSE + + Copyright (c) 2013 Enrique García Cota + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ]], +} + +local tostring = tostring +local setmetatable = setmetatable +local next = next +local type = type +local rawget = rawget +local getmetatable = getmetatable + +inspect.KEY = setmetatable({}, { + __tostring = function() + return "inspect.KEY" + end, +}) +inspect.METATABLE = setmetatable({}, { + __tostring = function() + return "inspect.METATABLE" + end, +}) + +local function rawpairs(t) + return next, t, nil +end + +-- Apostrophizes the string if it has quotes, but not aphostrophes +-- Otherwise, it returns a regular quoted string +local function smartQuote(str) + if str:match('"') and not str:match("'") then + return "'" .. str .. "'" + end + return '"' .. str:gsub('"', '\\"') .. '"' +end + +-- \a => '\\a', \0 => '\\0', 31 => '\31' +local shortControlCharEscapes = { + ["\a"] = "\\a", + ["\b"] = "\\b", + ["\f"] = "\\f", + ["\n"] = "\\n", + ["\r"] = "\\r", + ["\t"] = "\\t", + ["\v"] = "\\v", +} +local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031 +for i = 0, 31 do + local ch = string.char(i) + if not shortControlCharEscapes[ch] then + shortControlCharEscapes[ch] = "\\" .. i + longControlCharEscapes[ch] = string.format("\\%03d", i) + end +end + +local function escape(str) + return (str:gsub("\\", "\\\\"):gsub("(%c)%f[0-9]", longControlCharEscapes):gsub("%c", shortControlCharEscapes)) +end + +local function isIdentifier(str) + return type(str) == "string" and str:match("^[_%a][_%a%d]*$") +end + +local function isSequenceKey(k, sequenceLength) + return type(k) == "number" and 1 <= k and k <= sequenceLength and math.floor(k) == k +end + +local defaultTypeOrders = { + ["number"] = 1, + ["boolean"] = 2, + ["string"] = 3, + ["table"] = 4, + ["function"] = 5, + ["userdata"] = 6, + ["thread"] = 7, +} + +local function sortKeys(a, b) + local ta, tb = type(a), type(b) + + -- strings and numbers are sorted numerically/alphabetically + if ta == tb and (ta == "string" or ta == "number") then + return a < b + end + + local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb] + -- Two default types are compared according to the defaultTypeOrders table + if dta and dtb then + return defaultTypeOrders[ta] < defaultTypeOrders[tb] + elseif dta then + return true -- default types before custom ones + elseif dtb then + return false -- custom types after default ones + end + + -- custom types are sorted out alphabetically + return ta < tb +end + +-- For implementation reasons, the behavior of rawlen & # is "undefined" when +-- tables aren't pure sequences. So we implement our own # operator. +local function getSequenceLength(t) + local len = 1 + local v = rawget(t, len) + while v ~= nil do + len = len + 1 + v = rawget(t, len) + end + return len - 1 +end + +local function getNonSequentialKeys(t) + local keys, keysLength = {}, 0 + local sequenceLength = getSequenceLength(t) + for k, _ in rawpairs(t) do + if not isSequenceKey(k, sequenceLength) then + keysLength = keysLength + 1 + keys[keysLength] = k + end + end + table.sort(keys, sortKeys) + return keys, keysLength, sequenceLength +end + +local function countTableAppearances(t, tableAppearances) + tableAppearances = tableAppearances or {} + + if type(t) == "table" then + if not tableAppearances[t] then + tableAppearances[t] = 1 + for k, v in rawpairs(t) do + countTableAppearances(k, tableAppearances) + countTableAppearances(v, tableAppearances) + end + countTableAppearances(getmetatable(t), tableAppearances) + else + tableAppearances[t] = tableAppearances[t] + 1 + end + end + + return tableAppearances +end + +local copySequence = function(s) + local copy, len = {}, #s + for i = 1, len do + copy[i] = s[i] + end + return copy, len +end + +local function makePath(path, ...) + local keys = {...} + local newPath, len = copySequence(path) + for i = 1, #keys do + newPath[len + i] = keys[i] + end + return newPath +end + +local function processRecursive(process, item, path, visited) + if item == nil then + return nil + end + if visited[item] then + return visited[item] + end + + local processed = process(item, path) + if type(processed) == "table" then + local processedCopy = {} + visited[item] = processedCopy + local processedKey + + for k, v in rawpairs(processed) do + processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited) + if processedKey ~= nil then + processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited) + end + end + + local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited) + if type(mt) ~= "table" then + mt = nil + end -- ignore not nil/table __metatable field + setmetatable(processedCopy, mt) + processed = processedCopy + end + return processed +end + +------------------------------------------------------------------- + +local Inspector = {} +local Inspector_mt = { + __index = Inspector, +} + +function Inspector:puts(...) + local args = {...} + local buffer = self.buffer + local len = #buffer + for i = 1, #args do + len = len + 1 + buffer[len] = args[i] + end +end + +function Inspector:down(f) + self.level = self.level + 1 + f() + self.level = self.level - 1 +end + +function Inspector:tabify() + self:puts(self.newline, string.rep(self.indent, self.level)) +end + +function Inspector:alreadyVisited(v) + return self.ids[v] ~= nil +end + +function Inspector:getId(v) + local id = self.ids[v] + if not id then + local tv = type(v) + id = (self.maxIds[tv] or 0) + 1 + self.maxIds[tv] = id + self.ids[v] = id + end + return tostring(id) +end + +function Inspector:putKey(k) + if isIdentifier(k) then + return self:puts(k) + end + self:puts("[") + self:putValue(k) + self:puts("]") +end + +function Inspector:putTable(t) + if t == inspect.KEY or t == inspect.METATABLE then + self:puts(tostring(t)) + elseif self:alreadyVisited(t) then + self:puts("") + elseif self.level >= self.depth then + self:puts("{...}") + else + if self.tableAppearances[t] > 1 then + self:puts("<", self:getId(t), ">") + end + + local nonSequentialKeys, nonSequentialKeysLength, sequenceLength = getNonSequentialKeys(t) + local mt = getmetatable(t) + + self:puts("{") + self:down(function() + local count = 0 + for i = 1, sequenceLength do + if count > 0 then + self:puts(",") + end + self:puts(" ") + self:putValue(t[i]) + count = count + 1 + end + + for i = 1, nonSequentialKeysLength do + local k = nonSequentialKeys[i] + if count > 0 then + self:puts(",") + end + self:tabify() + self:putKey(k) + self:puts(" = ") + self:putValue(t[k]) + count = count + 1 + end + + if type(mt) == "table" then + if count > 0 then + self:puts(",") + end + self:tabify() + self:puts(" = ") + self:putValue(mt) + end + end) + + if nonSequentialKeysLength > 0 or type(mt) == "table" then -- result is multi-lined. Justify closing } + self:tabify() + elseif sequenceLength > 0 then -- array tables have one extra space before closing } + self:puts(" ") + end + + self:puts("}") + end +end + +function Inspector:putValue(v) + local tv = type(v) + + if tv == "string" then + self:puts(smartQuote(escape(v))) + elseif tv == "number" or tv == "boolean" or tv == "nil" or tv == "cdata" or tv == "ctype" then + self:puts(tostring(v)) + elseif tv == "table" then + self:putTable(v) + else + self:puts("<", tv, " ", self:getId(v), ">") + end +end + +------------------------------------------------------------------- + +function inspect.inspect(root, options) + options = options or {} + + local depth = options.depth or math.huge + local newline = options.newline or "\n" + local indent = options.indent or " " + local process = options.process + + if process then + root = processRecursive(process, root, {}, {}) + end + + local inspector = setmetatable({ + depth = depth, + level = 0, + buffer = {}, + ids = {}, + maxIds = {}, + newline = newline, + indent = indent, + tableAppearances = countTableAppearances(root), + }, Inspector_mt) + + inspector:putValue(root) + + return table.concat(inspector.buffer) +end + +setmetatable(inspect, { + __call = function(_, ...) + return inspect.inspect(...) + end, +}) + +return inspect diff --git a/framework/skynet b/framework/skynet new file mode 160000 index 0000000..056db8c --- /dev/null +++ b/framework/skynet @@ -0,0 +1 @@ +Subproject commit 056db8cf0f3d21cd3b34cf8f36976e00f26bc15f diff --git a/logs/.gitkeep b/logs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/server/public/config/readme.txt b/server/public/config/readme.txt new file mode 100644 index 0000000..a51a9f6 --- /dev/null +++ b/server/public/config/readme.txt @@ -0,0 +1 @@ +策划配置目录 \ No newline at end of file diff --git a/server/public/const/readme.txt b/server/public/const/readme.txt new file mode 100644 index 0000000..fe84ea1 --- /dev/null +++ b/server/public/const/readme.txt @@ -0,0 +1 @@ +服务器内部定义的 只读 变量 \ No newline at end of file diff --git a/server/public/crab/readme.txt b/server/public/crab/readme.txt new file mode 100644 index 0000000..a06f546 --- /dev/null +++ b/server/public/crab/readme.txt @@ -0,0 +1 @@ +敏感字过滤 \ No newline at end of file diff --git a/server/public/crab/test.lua b/server/public/crab/test.lua new file mode 100644 index 0000000..1f81d1b --- /dev/null +++ b/server/public/crab/test.lua @@ -0,0 +1,56 @@ +-- https://github.com/sundream/crab + +local crab = require "crab.c" +local utf8 = require "utf8.c" + +local words = {} +for line in io.lines("words.txt") do + local t = {} + assert(utf8.toutf32(line, t), "non utf8 words detected:"..line) + table.insert(words, t) +end + +local filter = crab.open(words) +local input = io.input("texts.txt"):read("*a") +local texts = {} +assert(utf8.toutf32(input, texts), "non utf8 words detected:", texts) +filter:filter(texts) +local output = utf8.toutf8(texts) +print(output) + +-- 将替换字符改成'#',过滤区间排除前后60个字 +-- 另外敏感字在排除单词集中不被替换 +words = {"一定","无论遇到"} +for i,word in ipairs(words) do + local t = {} + assert(utf8.toutf32(word, t), "non utf8 words detected:"..word) + words[i] = t +end +-- crab.new is alias of crab.open +local excluder = crab.new(words) +print("default replace_rune:",filter:replace_rune()) +filter:replace_rune(0x23) +print("new replace_rune:",filter:replace_rune()) + +texts = {} +assert(utf8.toutf32(input, texts), "non utf8 words detected:", texts) +local from = 60 +local to = #texts - 60 + +local filter_range = {} +local start = from +local found,pos1,pos2 = excluder:next(texts,start,to) +while found do + if pos1 > start then + table.insert(filter_range,{start,pos1-1}) + end + start = pos2 + 1 + found,pos1,pos2 = excluder:next(texts,start,to); +end +table.insert(filter_range,{start,to}) + +for _,t in ipairs(filter_range) do + filter:filter(texts,t[1],t[2]); +end +output = utf8.toutf8(texts) +print(output) diff --git a/server/public/crab/texts.txt b/server/public/crab/texts.txt new file mode 100644 index 0000000..d947656 --- /dev/null +++ b/server/public/crab/texts.txt @@ -0,0 +1,48 @@ +那些意外,似乎来的早了些,也只能听从心的指引,去做到最好。 + +无论生死,还是离别,一次,真的足够了。 + +如果我不能带给你平静的喜乐,请你一定要远离我, + +若你还爱我,舍不得,请珍惜我,给我安宁与平静的欢喜。 + +单薄的衣衫,翩跹在春风里,似乎有点瑟瑟。 + +凝眸花草,我彻底的寂寞着,有点心酸,有点心动,依然还有回眸的幸福。 + +床头的旧照片,还是我从电脑上临摹的,你温暖的眼神依旧,仿佛注视着我。 + +我爱极了瞳孔里的深邃,无眠的夜晚,我常常悄悄的与那个你对着话,也不知道聊着什么。 + +抚摸着那个轮廓,很近,又很远,依然不能阻止那些温暖,仿佛就在身边,在心底升腾着笑意。 + +心有所定,只是专注着,在人群中静静独立着,保持着一种完整的姿态,等待着。 + +从不是盲目的期待,也不是纠缠,我知道,我一直遇见着那颗心,那颗总能让我温暖的心。 + +或许,太过执着,总有些患得患失,心底寂静无声依赖着。 + +现世依旧安稳着,静静相守,一个人愿意跟另一个人一起老去,是一种最美的承诺。 + +我庆幸着,我没有一个人相思着,我爱着的人,如此爱着我。 + +这些寂寞,似乎是些折磨,又似乎是幸福,我可以有足够的时间,慢慢的想,慢慢的等。 + +相爱,是一场真心的对奕,从不怕辜负,我只怕我不能带给你快乐,若我不能,你一定要离开。 + +我的内心,那么的希望,你能找寻到自己的快乐。 + +梨花似雪的心情,遥望着,这个世界上,即使再阴暗的角落,也会有爱情的微光照耀。 + +我们终其一生寻找的,或许就是灵魂的安放地,不找到是无法罢休的。 + +或许,只是一种形式的亲近,亦或是一种心的贴近,有心,用心,安心,足够了。 + +人的一生,何其漫长,纵然遇到再多的人,那个对的只有你。 + +无论遇到谁,我已然没有爱的能力了,一次足够了,他们都不是你。 + +无论是否适合,我知道,自己的内心如此固执的深爱着。 + +凡此世间种种,或者都是因果循环。 + diff --git a/server/public/crab/words.txt b/server/public/crab/words.txt new file mode 100644 index 0000000..f8b3af2 --- /dev/null +++ b/server/public/crab/words.txt @@ -0,0 +1,18 @@ +意外 +有点 +有点心动 +我一直 +一个人 +一种 +一种麦子 +一 +可以有足够的时间 +无论 +生死 +还是 +离别 +一次 +真的足够了 +从不 +盲目 +期待 diff --git a/server/public/enum/readme.txt b/server/public/enum/readme.txt new file mode 100644 index 0000000..bceb3d9 --- /dev/null +++ b/server/public/enum/readme.txt @@ -0,0 +1 @@ +服务器内部定义的 枚举 \ No newline at end of file diff --git a/server/public/protocol/readme.txt b/server/public/protocol/readme.txt new file mode 100644 index 0000000..2ed075c --- /dev/null +++ b/server/public/protocol/readme.txt @@ -0,0 +1 @@ +协议目录 \ No newline at end of file diff --git a/server/public/readme.txt b/server/public/readme.txt new file mode 100644 index 0000000..7d24db9 --- /dev/null +++ b/server/public/readme.txt @@ -0,0 +1 @@ +公共配置存放目录 \ No newline at end of file diff --git a/server/spec/readme.txt b/server/spec/readme.txt new file mode 100644 index 0000000..b235f28 --- /dev/null +++ b/server/spec/readme.txt @@ -0,0 +1 @@ +各个服务单元测试目录 \ No newline at end of file diff --git a/server/src/helper/readme.txt b/server/src/helper/readme.txt new file mode 100644 index 0000000..9d9a403 --- /dev/null +++ b/server/src/helper/readme.txt @@ -0,0 +1 @@ +内部使用的 接口文件 \ No newline at end of file diff --git a/server/src/node/game/ws_agent.lua b/server/src/node/game/ws_agent.lua new file mode 100644 index 0000000..2b84590 --- /dev/null +++ b/server/src/node/game/ws_agent.lua @@ -0,0 +1,44 @@ +local skynet = require "skynet" +require "tracing.init" + +local CMD = {} + +local CLIENT_FD +local GATE +-- local WATCHDOG + +skynet.register_protocol { + name = "client", + id = skynet.PTYPE_CLIENT, + unpack = skynet.tostring, + dispatch = function(fd, _, msg) + assert(fd == CLIENT_FD) -- You can use fd to reply message + skynet.ignoreret() -- session is fd, don't call skynet.ret + skynet.tracing_send(GATE, { + cmd = "response", + }, CLIENT_FD, msg) + end, +} + +function CMD.start(tracer, conf) + local fd = conf.client + GATE = conf.gate + -- WATCHDOG = conf.watchdog + CLIENT_FD = fd + + skynet.tracing_call(GATE, "forward", tracer, fd) +end + +-- tracer +function CMD.disconnect(_) + -- todo: do something before exit + skynet.exit() +end + +skynet.start(function() + skynet.dispatch("lua", function(_, _, cmd, tracer, ...) + skynet.trace(tracer) + local f = CMD[cmd] + skynet.ret(skynet.pack(f(tracer, ...))) + end) +end) diff --git a/server/src/node/game/ws_gate.lua b/server/src/node/game/ws_gate.lua new file mode 100644 index 0000000..c9791d8 --- /dev/null +++ b/server/src/node/game/ws_gate.lua @@ -0,0 +1,236 @@ +local skynet = require "skynet" +require "skynet.manager" +require "tracing.init" + +local socket = require "skynet.socket" +local websocket = require "http.websocket" +local socketdriver = require "skynet.socketdriver" + +local watchdog +local connection = {} -- fd -> connection : { fd , client, agent , ip, mode } +local forwarding = {} -- agent -> connection + +local client_number = 0 +local maxclient +local nodelay +local protocol + +local master_name, slave_name = ... +master_name = master_name or ".ws_gate" + +skynet.register_protocol { + name = "client", + id = skynet.PTYPE_CLIENT, +} + +local function launch_master() + local CMD_MASTER = {} + local slave = {} + + function CMD_MASTER.open(source, tracer, conf) + local instance = conf.instance or 8 + assert(instance > 0) + + for i = 1, instance do + local _slave_name = string.format("%s-slave-%d", master_name, i) + table.insert(slave, skynet.newservice(SERVICE_NAME, master_name, _slave_name)) + end + + conf.watchdog = conf.watchdog or source + for i = 1, instance do + local s = slave[i] + skynet.tracing_call(s, "open", tracer, conf) + end + + local address = conf.address or "0.0.0.0" + local port = assert(conf.port) + protocol = conf.protocol or "ws" + + local balance = 1 + + local fd = socket.listen(address, port) + skynet.error(string.format("Listen websocket port:%s protocol:%s", port, protocol)) + socket.start(fd, function(socket_fd, addr) + skynet.error(string.format("accept client socket_fd: %s addr:%s", socket_fd, addr)) + + local s = slave[balance] + balance = balance + 1 + local ok, err = skynet.tracing_call(s, "accept", {}, socket_fd, addr) + if not ok then + skynet.error(string.format("invalid client (socket_fd = %d) error = %s", socket_fd, err)) + end + end) + end + + skynet.dispatch("lua", function(session, source, cmd, tracer, ...) + skynet.trace(tracer) + + local f = CMD_MASTER[cmd] + if not f then + skynet.error("ws gate master can't dispatch cmd " .. (cmd or nil)) + skynet.ret(skynet.pack({ + ok = false, + })) + return + end + if session == 0 then + f(source, tracer, ...) + else + skynet.ret(skynet.pack(f(source, tracer, ...))) + end + end) +end + +local function launch_slave() + local function unforward(c) + if c.agent then + forwarding[c.agent] = nil + c.agent = nil + c.client = nil + end + end + + local function close_fd(fd) + local c = connection[fd] + if c then + unforward(c) + connection[fd] = nil + client_number = client_number - 1 + end + end + + local handler = {} + + function handler.connect(fd) + skynet.error("ws connect from: " .. tostring(fd)) + if client_number >= maxclient then + socketdriver.close(fd) + return + end + if nodelay then + socketdriver.nodelay(fd) + end + + client_number = client_number + 1 + local addr = websocket.addrinfo(fd) + local c = { + fd = fd, + ip = addr, + } + connection[fd] = c + + skynet.tracing_send(watchdog, { + cmd = "socket", + }, "open", fd, addr, skynet.self()) + end + + function handler.handshake(fd, header, url) + local addr = websocket.addrinfo(fd) + skynet.error("ws handshake from: " .. tostring(fd), "url", url, "addr:", addr, " header:", header) + end + + function handler.message(fd, msg) + skynet.error("ws ping from: " .. tostring(fd), msg .. "\n") + -- recv a package, forward it + local c = connection[fd] + local agent = c and c.agent + -- msg is string + if agent then + skynet.redirect(agent, c.client, "client", fd, msg) + else + skynet.tracing_send(watchdog, { + cmd = "socket", + }, "data", fd, msg) + end + end + + function handler.ping(fd) + skynet.error("ws ping from: " .. tostring(fd) .. "\n") + end + + function handler.pong(fd) + skynet.error("ws pong from: " .. tostring(fd)) + end + + function handler.close(fd, code, reason) + skynet.error("ws close from: " .. tostring(fd), code, reason) + close_fd(fd) + skynet.tracing_send(watchdog, { + cmd = "socket", + }, "close", fd) + end + + function handler.error(fd) + skynet.error("ws error from: " .. tostring(fd)) + close_fd(fd) + skynet.tracing_send(watchdog, { + cmd = "socket", + }, "error", fd) + end + + function handler.warning(fd, size) + skynet.tracing_send(watchdog, { + cmd = "socket", + }, "warning", fd, size) + end + + local CMD_SLAVE = {} + function CMD_SLAVE.forward(source, _, fd, client, address) + local c = assert(connection[fd]) + unforward(c) + c.client = client or 0 + c.agent = address or source + forwarding[c.agent] = c + end + + function CMD_SLAVE.response(_, _, fd, msg) + skynet.error("ws response: " .. tostring(fd), msg .. "\n") + -- forward msg + websocket.write(fd, msg) + end + + function CMD_SLAVE.kick(_, _, fd) + websocket.close(fd) + end + + function CMD_SLAVE.accept(_, _, fd, addr) + return websocket.accept(fd, handler, protocol, addr) + end + + function CMD_SLAVE.open(source, _, conf) + maxclient = conf.maxclient or 1024 + nodelay = conf.nodelay + protocol = conf.protocol or "ws" + watchdog = conf.watchdog + skynet.error("open", watchdog, source) + end + + skynet.dispatch("lua", function(session, source, cmd, tracer, ...) + skynet.trace(tracer) + local f = CMD_SLAVE[cmd] + if not f then + skynet.error("ws gate slave can't dispatch cmd " .. (cmd or nil)) + skynet.ret(skynet.pack({ + ok = false, + })) + return + end + if session == 0 then + f(source, tracer, ...) + else + skynet.ret(skynet.pack(f(source, tracer, ...))) + end + end) +end + +skynet.start(function() + if slave_name then + skynet.error("start ws_gate slave:", slave_name) + skynet.register(slave_name) + launch_slave() + else + skynet.error("start ws_gate master:", master_name) + skynet.register(master_name) + launch_master() + end +end) diff --git a/server/src/node/game/ws_watchdog.lua b/server/src/node/game/ws_watchdog.lua new file mode 100644 index 0000000..5e98412 --- /dev/null +++ b/server/src/node/game/ws_watchdog.lua @@ -0,0 +1,93 @@ +local skynet = require "skynet" +require "tracing.init" +local cluster = require "skynet.cluster" + +local CMD = {} +local SOCKET = {} +local master_gate +local agent = {} +local protocol +local fd2gate = {} + +function SOCKET.open(tracer, fd, addr, gate) + skynet.error("New client from : " .. addr) + fd2gate[fd] = gate + local agent_addr = skynet.newservice("ws_agent") + agent[fd] = agent_addr + skynet.tracing_call( + agent_addr, + "start", + tracer, + { + gate = gate, + client = fd, + watchdog = skynet.self(), + protocol = protocol, + addr = addr + } + ) +end + +local function close_agent(tracer, fd) + local a = agent[fd] + agent[fd] = nil + if a then + local gate = fd2gate[fd] + if gate then + skynet.tracing_call(gate, "kick", tracer, fd) + fd2gate[fd] = nil + end + -- disconnect never return + tracer.cmd = "disconnect" + skynet.tracing_send(a, tracer) + end +end + +function SOCKET.close(tracer, fd) + close_agent(tracer, fd) +end + +function SOCKET.error(tracer, fd) + close_agent(tracer, fd) +end + +function SOCKET.warning(_, fd, size) + -- size K bytes havn't send out in fd + skynet.error("socket warning", fd, size) +end + +function SOCKET.data(_, fd, msg) + skynet.error("socket data", fd, msg) +end + +function CMD.start(tracer, conf) + protocol = conf.protocol + skynet.tracing_call(master_gate, "open", tracer, conf) + cluster.tracing_call("proxy_1", ".test_proxy", "test_proxy", tracer, 100) +end + +function CMD.close(_, fd) + close_agent(fd) +end + +skynet.start( + function() + skynet.dispatch( + "lua", + function(_, _, cmd, tracer, subcmd, ...) + skynet.trace(tracer) + + if cmd == "socket" then + -- socket api don't need return + local f = SOCKET[subcmd] + f(tracer, ...) + else + local f = assert(CMD[cmd]) + skynet.ret(skynet.pack(f(tracer, subcmd, ...))) + end + end + ) + + master_gate = skynet.newservice("ws_gate") + end +) diff --git a/server/src/node/readme.txt b/server/src/node/readme.txt new file mode 100644 index 0000000..380fabf --- /dev/null +++ b/server/src/node/readme.txt @@ -0,0 +1 @@ +节点类型目录文件 \ No newline at end of file diff --git a/server/src/preload/env.lua b/server/src/preload/env.lua new file mode 100644 index 0000000..3f4c332 --- /dev/null +++ b/server/src/preload/env.lua @@ -0,0 +1,4 @@ +return { + DEBUG = true, + NODEID = 1, +} diff --git a/server/src/preload/preload.lua b/server/src/preload/preload.lua new file mode 100644 index 0000000..1de393a --- /dev/null +++ b/server/src/preload/preload.lua @@ -0,0 +1,11 @@ +collectgarbage("setpause", 120) -- 内存增大 1 倍(200/100)时自动释放一次内存 (200 是默认值) +collectgarbage("setstepmul", 1000) -- 收集器单步收集的速度相对于内存分配速度的倍率,设置 200 的倍率等于 2 倍(200/100)。(200 是默认值) + +class = require("oop.middleclass") +handler = require("oop.handler") -- 支持 function 和 对象内部 handler 不过后者热更会更友好 +gEnv = require("env") + +if gEnv.DEBUG then + require("tracing.skynet") + require("swt.debug") +end diff --git a/server/src/readme.txt b/server/src/readme.txt new file mode 100644 index 0000000..bb1481b --- /dev/null +++ b/server/src/readme.txt @@ -0,0 +1 @@ +项目代码目录 \ No newline at end of file diff --git a/server/src/service/readme.txt b/server/src/service/readme.txt new file mode 100644 index 0000000..e69de29 diff --git a/shell/.gitkeep b/shell/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tools/LuaMacro/config.ld b/tools/LuaMacro/config.ld new file mode 100644 index 0000000..40e3744 --- /dev/null +++ b/tools/LuaMacro/config.ld @@ -0,0 +1,8 @@ +project = 'LuaMacro' +format = 'markdown' +new_type("macro","Macros") +output = 'api' +alias("p","param") +file = { + 'macro.lua','macro','luam.lua', +} diff --git a/tools/LuaMacro/docgen b/tools/LuaMacro/docgen new file mode 100644 index 0000000..f8765d3 --- /dev/null +++ b/tools/LuaMacro/docgen @@ -0,0 +1,3 @@ +ldoc . +lua markdown.lua readme.md +cp readme.html docs/index.html diff --git a/tools/LuaMacro/docgen.bat b/tools/LuaMacro/docgen.bat new file mode 100644 index 0000000..509bc1f --- /dev/null +++ b/tools/LuaMacro/docgen.bat @@ -0,0 +1 @@ +ldoc . && lua ..\winapi\markdown.lua readme.md && copy readme.html docs\index.html diff --git a/tools/LuaMacro/luam b/tools/LuaMacro/luam new file mode 100755 index 0000000..bd02269 --- /dev/null +++ b/tools/LuaMacro/luam @@ -0,0 +1,280 @@ +#!/usr/bin/env lua +--[[-- +Front end for LuaMacro, a Lua macro preprocessor. + +The default action is to preprocess and run a Lua file. To just dump +the preprocessed output, use the `-d` flag. Like `lua`, the `-l` flag can +be used to load a library first, but you need to explicitly say `-i` to +get an interactive prompt. + +The package loader is modified so that `require 'mod'` will preprocess `mod` if it is found as `mod.m.lua`. + +Dumping is the only action available when preprocessing C code with `-C`. + +@script luam +]] + +-- adjust the path so that this script can see the macro package +local path = arg[0]:gsub('[^/\\]+$','') +package.path = package.path .. ';' .. path .. '?.lua;'..path .. 'macro/?.lua' +local macro = require 'macro' +require 'macro.builtin' + +--- Using luam. +-- @usage follows +local usage = [[ +LuaMacro 2.5.0, a Lua macro preprocessor and runner + -l require a library + -e statement to be executed + -V set a variable (-VX or -VY=1) + -c error context to be shown (default 2) + -d dump preprocessed output to stdout + -o write to this file + -C C lexer + -N No #line directives when generating C + -i interactive prompt + -v verbose error trace + Lua source file +]] + +-- parsing the args, the hard way: +local takes_value = {l = '', e = '', c = 2, o = '',V = ';'} + +local args = {} +local idx,i = 1,1 +while i <= #arg do + local a = arg[i] + local flag = a:match '^%-(.+)' + local val + if flag then + if #flag > 1 then -- allow for -lmod, like Lua + val = flag:sub(2) + flag = flag:sub(1,1) + end + -- grab the next argument if we need a value + if takes_value[flag] and not val then + i = i + 1 + val = arg[i] + end + -- convert the argument, if required + local def = takes_value[flag] + if type(def) == 'number' then + val = tonumber(val) + elseif def == ';' and args[flag] then + val = args[flag]..';'..val + end + args[flag] = val or true + else + args[idx] = a + idx = idx + 1 + end + i = i + 1 +end + +if not args[1] and not args.i then + print(usage) + os.exit() +elseif args[1] then + args.input_name = args[1] + args.input,err = io.open(args[1],'r') + if err then return print(err) end + table.remove(args,1) +end +-- set defaults, if flags not specified +for k,v in pairs(takes_value) do + if not args[k] then + args[k] = v + end +end + +---------- compiling and running the output ------ +-- the tricky bit here is presenting the errors so that they refer to the +-- original line numbers. In addition, we also present a few lines of context +-- in the output. + +local function lookup_line (lno,li) + for i = 1,#li-1 do + --print(li[i].il,li[i].ol,lno,'match') + if lno < li[i+1].ol then + return li[i].il + (lno - li[i].ol) - 1 + end + end + return li[#li].il + (lno - li[#li].ol) - 1 +end + +-- iterating over all lines in a string can be awkward; +-- gmatch doesn't handle the empty-line cases properly. +local function split_nl (t) + local k1 = 1 + local k2 = t:find ('[\r\n]',k1) + return function() + if not k2 then return nil end + local res = t:sub(k1,k2-1) + k1 = k2+1 + k2 = t:find('[\r\n]',k1) + return res + end +end + +local function fix_error_trace (err,li) + local strname,lno = err:match '%[string "(%S+)"%]:(%d+)' + local ino + if strname then + lno = tonumber(lno) + if li then + ino = lookup_line(lno,li) + err = err:gsub('%[string "%S+"%]:'..(lno or '?')..':',strname..':'..(ino or '?')) + end + end + return err,lno,ino +end + +local function runstring (code,name,li,...) + local res,err = loadstring(code,name) + local lno,ok + if not res then + err,lno,ino = fix_error_trace(err,li) + if ino then + print 'preprocessed context of error:' + local l1,l2 = lno-args.c,lno+args.c + local l = 1 + for line in split_nl(code) do + if l >= l1 and l <= l2 then + if l == lno then io.write('*') else io.write(' ') end + print(l,line) + end + l = l + 1 + end + end + io.stderr:write(err,'\n') + os.exit(1) + end + ok,err = xpcall(function(...) return res(...) end, debug.traceback) + if not ok then + err = err:gsub("%[C%]: in function 'xpcall'.+",'') + if li then + repeat + err,lno = fix_error_trace(err,li) + until not lno + end + io.stderr:write(err,'\n') + end + return ok +end + +local function subst (ins,name) + local C + if args.C then + C = args.N and true or 'line' + end + return macro.substitute_tostring(ins,name,C,args.v) +end + +local function subst_runstring (ins,name,...) + local buf,li = subst(ins,name) + if not buf then + io.stderr:write(li,'\n') + os.exit(1) + end + if args.d or args.C or args.o ~= '' then + if args.o == '' then + print(buf) + else + local f = io.open(args.o,'w') + f:write(buf) + f:close() + end + else + return runstring(buf,name,li,...) + end +end + +-- Lua 5.1/5.2 compatibility +local pack = table.pack +if not pack then + function pack(...) + return {n=select('#',...),...} + end +end +if not unpack then unpack = table.unpack end + +local function eval(code) + local status,val,f,err,rcnt + code,rcnt = code:gsub('^%s*=','return ') + f,err = loadstring(code,'TMP') + if f then + res = pack(pcall(f)) + if not res[1] then err = res[2] + else + return res + end + end + if err then + err = tostring(err):gsub('^%[string "TMP"%]:1:','') + return {nil,err} + end +end + +local function interactive_loop () + os.execute(arg[-1]..' -v') -- for the Lua copyright + print 'Lua Macro 2.5.0 Copyright (C) 2007-2011 Steve Donovan' + + local function readline() + io.write(_PROMPT or '> ') + return io.read() + end + + require 'macro.all' + _G.macro = macro + macro.define 'quit os.exit()' + macro._interactive = true + + local line = readline() + while line do + local s,err = subst(line..'\n') + if not s then + err = err:gsub('.-:%d+:','') + print('macro error: '..err) + elseif not s:match '^%s*$' then + if args.d then print(s) end + local res = eval(s) + if not res[1] then + print('expanded: '..s) + print('error: '..res[2]) + elseif res[2] ~= nil then + print(unpack(res,2)) + end + end + line = readline() + end +end + +macro.set_package_loader() + +if args.l ~= '' then require(args.l) end + +if args.V ~= ';' then + for varset in args.V:gmatch '([^;]+)' do + local sym,val = varset:match '([^=]+)=(.+)' + if not sym then + sym = varset + val = true + end + _G[sym] = val + end +end + +require 'macro.ifelse' + +if args.e ~= '' then + subst_runstring(args.e,"") +else + if args.input then + arg = args + arg[0] = args.input_name + arg[-1] = 'luam' + subst_runstring(args.input,args.input_name,unpack(args)) + elseif args.i then + interactive_loop() + end +end diff --git a/tools/LuaMacro/macro.lua b/tools/LuaMacro/macro.lua new file mode 100644 index 0000000..6c1a38d --- /dev/null +++ b/tools/LuaMacro/macro.lua @@ -0,0 +1,713 @@ +---------------------------------------------- +-- LuaMacro 2, a macro-preprocessor for Lua. +-- Unlike LuaMacro 1.x, it does not depend on the token-filter patch and generates +-- Lua code which can be printed out or compiled directly. C-style macros are easy, but LM2 +-- allows for macros that can read their own input and generate output using Lua code. +-- New in this release are lexically-scoped macros. +-- The Lua Lpeg Lexer is by Peter Odding. +-- +-- Examples: +-- +-- macro.define 'sqr(x) ((x)*(x))' +-- macro.define 'assert_(expr) assert(expr,_STR_(expr))' +-- macro.define('R(n)',function(n) +-- n = n:get_number() +-- return ('-'):rep(n) +-- end +-- macro.define('lazy',function(get) +-- get() -- skip space +-- local expr,endt = get:upto(function(t,v) +-- return t == ',' or t == ')' or t == ';' +-- or (t=='space' and v:match '\n') +-- end) +-- return 'function(_) return '..tostring(expr)..' end'..tostring(endt) +-- end) +-- +-- +-- @author Steve Donovan +-- @copyright 2011 +-- @license MIT/X11 +-- @module macro +-- @alias M + +local macro = {} +local M = macro +local lexer = require 'macro.lexer' +local Getter = require 'macro.Getter' +local TokenList = require 'macro.TokenList' +local scan_code = lexer.scan_lua +local append = table.insert +local setmetatable = setmetatable + +--local tdump = require 'pl.pretty'.dump + +local scan_iter, tnext = Getter.scan_iter, Getter.next + + +M.upto_keywords = Getter.upto_keywords +M.Putter = TokenList.new + +-- given a token list, a set of formal arguments and the actual arguments, +-- return a new token list where the formal arguments have been replaced +-- by the actual arguments +local function substitute_tokenlist (tl,parms,args) + local append,put_tokens = table.insert,TokenList.tokens + local parm_map = {} + for i,name in ipairs(parms) do + parm_map[name] = args[i] + end + local res = {} + for _,tv in ipairs(tl) do + local t,v = tv[1],tv[2] + if t == 'iden' then + local pval = parm_map[v] + if pval then + put_tokens(res,pval) + else + append(res,tv) + end + else + append(res,tv) + end + end + return res +end + +---------------- +-- Defining and Working with Macros. +-- @section macros + +--- make a copy of a list of tokens. +-- @param tok the list +-- @param pred copy up to this condition; if defined, must be a function +-- of two arguments, the token type and the token value. +-- @return the copy +-- @return the token that matched the predicate +function M.copy_tokens(tok,pred) + local res = {} + local t,v = tok() + while t and not (pred and pred(t,v)) do + append(res,{t,v}) + t,v = tok() + end + return res,{t,v} +end + +---- define new lexical tokens. +-- @param extra a list of strings defining the new tokens +-- @usage macro.define_tokens{'{|'} +function M.define_tokens(extra) + lexer.add_extra_tokens(extra) +end + +local imacros,smacros = {},{} + +M.macro_table = imacros + +--- define a macro using a specification string and optional function. +-- The specification looks very much like a C preprocessor macro: the name, +-- followed by an optional formal argument list (_no_ space after name!) and +-- the substitution. e.g `answer 42` or `sqr(x) ((x)*(x))` +-- +-- If there is no substitution, then the second argument must be a function which +-- will be evaluated for the actual substitution. If there are explicit parameters, then they will be passed as token lists. Otherwise, the function is passed a `get` and a `put` argument, which are `Getter` and `TokenList` objects. +-- +-- The substitution function may return a `TokenList` object, or a string. +-- @param macstr +-- @param subst_fn the optional substitution function +-- @see macro.Getter, macro.TokenList +function M.define(macstr,subst_fn) + local tok,t,macname,parms,parm_map + local mtbl + tok = scan_code(macstr) + t,macname = tok() + if t == 'iden' then mtbl = imacros + elseif t ~= 'string' and t ~= 'number' and t ~= 'keyword' then + mtbl = smacros + else + error("a macro cannot be of type "..t) + end + t = tok() + if t == '(' then + parms = Getter.new(tok):idens() + end + mtbl[macname] = { + name = macname, + subst = subst_fn or M.copy_tokens(tok), + parms = parms + } +end + +--- define a macro using a function and a parameter list. +-- @param name either an identifier or an operator. +-- @param subst a function +-- @param parms a list of parameter names +-- @return the existing value of this macro, if any +function M.set_macro(name,subst,parms) + local macros + if name:match '^[_%a][_%w]*$' then + macros = imacros + else + macros = smacros + end + if subst == nil then + macros[name] = nil + return + end + local last = macros[name] + if type(subst) ~= 'table' or not subst.name then + subst = { + name = name, + subst = subst, + parms = parms + } + end + macros[name] = subst + return last +end + +--- defined a scoped macro. Like define except this macro will not +-- be visible outside the current scope. +-- @param name either an identifier or an operator. +-- @param subst a function +-- @param parms a list of parameter names +-- @see set_macro +function M.define_scoped (name,subst,parms) + local old_value = M.set_macro(name,subst,parms) + M.block_handler(-1,function() + M.set_macro(name,old_value) + end) +end + +--- get the value of a macro. The macro substitution must either be a +-- a string or a single token. +-- @param name existing macro name +-- @return a string value, or nil if the macro does not exist. +function M.get_macro_value(name) + local mac = imacros[name] + if not mac then return nil end + if type(mac.subst) == 'table' then + return mac.subst[1][2] + else + return mac.subst + end +end + +local function get_macro (mac, no_error) + local macro = imacros[mac] + if not macro and not no_error then + M.error("macro "..mac.." is not defined") + end + return macro +end + +local push,pop = table.insert,table.remove + +--- push a value on the stack associated with a macro. +-- @param name macro name +-- @param value any value +function M.push_macro_stack (name,value) + local macro = get_macro(name) + macro.stack = macro.stack or {} + push(macro.stack,value) +end + +--- pop a value from a macro stack. +-- @param name macro name +-- @return any value, if defined +function M.pop_macro_stack (name) + local macro = get_macro(name) + if macro.stack and #macro.stack > 0 then + return pop(macro.stack) + end +end + +--- value of top of macro stack. +-- @param name macro name +-- @return any value, if defined +function M.value_of_macro_stack (name) + local macro = get_macro(name,true) + if not macro then return nil end + if macro.stack and #macro.stack > 0 then + return macro.stack[#macro.stack] + end +end + +local lua_keywords = { + ['do'] = 'open', ['then'] = 'open', ['else'] = 'open', ['function'] = 'open', + ['repeat'] = 'open'; + ['end'] = 'close', ['until'] = 'close',['elseif'] = 'close' +} + +local c_keywords = {} +local keywords = lua_keywords + +local block_handlers,keyword_handlers = {},{} +local level = 1 + +--- specify a block handler at a given level. +-- a block handler may indicate with an extra true return +-- that it wants to persist; it is passed the getter and the keyword +-- so we can get more specific end-of-block handlers. +-- @param lev relative block level +-- @param action will be called when the block reaches the level +function M.block_handler (lev,action) + lev = lev + level + if not block_handlers[lev] then + block_handlers[lev] = {} + end + append(block_handlers[lev],action) +end + +local function process_block_handlers(level,get,v) + local persist,result + for _,bh in pairs(block_handlers[level]) do + local res,keep = bh(get,v) + if not keep then + if res then result = res end + else + persist = persist or {} + append(persist,bh) + end + end + block_handlers[level] = persist + return result +end + + +--- set a keyword handler. Unlike macros, the keyword itself is always +-- passed through, but the handler may add some output afterwards. +-- If the action is nil, then the handler for that keyword is removed. +-- @param word keyword +-- @param action function to be called when keyword is encountered +-- @return previous handler associated with this keyword +function M.keyword_handler (word,action) + if word == 'BEGIN' or word == 'END' then + keyword_handlers[word] = action + return + end + if action then + local last = keyword_handlers[word] + keyword_handlers[word] = action + return last + else + keyword_handlers[word] = nil + end +end + +--- set a scoped keyword handler. Like keyword_handler, except +-- it restores the original keyword handler (if any) at the end +-- of the current block. +-- @param word keyword +-- @param action to be called when keyword is encountered +-- @see keyword_handler +function M.scoped_keyword_handler (keyword, action) + local last = M.keyword_handler(keyword,action) + M.block_handler(-1,function() + M.keyword_handler(keyword,last) + end) +end + +-- a convenient way to use keyword handlers. This sets a handler and restores +-- the old handler at the end of the current block. +-- @param word keyword +-- @param action to be called when keyword is encountered +-- @return a function that creates a scoped keyword handler +function M.make_scoped_handler(keyword,handler) + return function() M.scoped_keyword_handler(keyword, action) end +end + +M.please_throw = false + +--- macro error messages. +-- @param msg the message: will also have file:line. +function M.error(msg) + msg = M.filename..':'..lexer.line..': '..msg + if M.please_throw then + error(msg,2) + else + io.stderr:write(msg,'\n') + os.exit(1) + end +end + +M.define ('debug_',function() + M.DEBUG = true +end) + +--- macro error assert. +-- @param expr an expression. +-- @param msg a message +function M.assert(expr,msg) + if not expr then M.error(msg or 'internal error') end + return expr +end + +Getter.error = M.error +Getter.assert = M.assert +TokenList.assert = M.assert + +local line_updater, line_table, last_name, last_lang + +local function lua_line_updater (iline,oline) + if not line_table then line_table = {} end + append(line_table,{il=iline,ol=oline}) +end + +local function c_line_updater (iline,oline,last_t,last_v) + local endt = last_t == 'space' and last_v or '\n' + return '#line '..iline..' "'..M.filename..'"'..endt +end + +local make_putter = TokenList.new + +--- do a macro substitution on Lua source. +-- @param src Lua source (either string or file-like reader) +-- @param out output (a file-like writer) +-- @param name input file name +-- @param use_c nil for Lua; if 'line', then output #line directives; if true, then don't +-- @return the result as table of strings +-- @return line number information +function M.substitute(src,name, use_c) + local out, ii = {}, 1 + local subparse + if name then + last_name = name + last_lang = use_c + else + name = last_name + use_c = last_lang and true + subparse = true + end + M.filename = name + if use_c then + lexer = require 'macro.clexer' + scan_code = lexer.scan_c + keywords = c_keywords + if use_c == 'line' then + line_updater = c_line_updater + else + line_updater = function() end + end + else + lexer = require 'macro.lexer' + scan_code = lexer.scan_lua + keywords = lua_keywords + line_updater = lua_line_updater + end + local tok = scan_code(src,name) + local iline,iline_changed = 0 + local last_t,last_v = 'space','\n' + local do_action + + + local t,v = tok() + + -- this function get() is always used, so that we can handle end-of-stream properly. + -- The substitution mechanism pushes a new stream on the tstack, which is popped + -- when empty. + local tstack = {} + local push,pop = table.insert,table.remove + + local function get () + last_t,last_v = t,v + local t,v = tok() + while not t do + tok = pop(tstack) + if tok == nil then + if not subparse and keyword_handlers.END then + do_action(keyword_handlers.END) + keyword_handlers.END = nil + end + if tok == nil then -- END action might have inserted some tokens + return nil + end + end -- finally finished + t,v = tok() + end + if name == lexer.name and iline ~= lexer.line then + iline = lexer.line -- input line has changed + iline_changed = last_v + end + return t,v + end + + local getter = Getter.new(get) + + --- get a list of consecutive matching tokens. + -- @param get token fetching function + -- @param accept set of token types (default: `{space=true,comment=true}`) + function getter.matching (get, accept) + accept = accept or {space=true, comment=true} + local tl = TokenList.new() + local t,v = get:peek(1, true) + while accept[t] do + t,v = get () + append(tl, {t, v}) + t,v = get:peek(1, true) + end + return tl + end + + function getter:peek (offset,dont_skip) + local step = offset < 0 and -1 or 1 -- passing offset 0 is undefined + local k = 0 + local token, t, v + repeat + while true do + token = tok (k) + if not token then return nil, 'EOS' end + t,v = token[1], token[2] + if dont_skip or (t ~= 'space' and t ~= 'comment') then break end + k = k + 1 + end + offset = offset - step + k = k + step + until offset == 0 + return t,v,k+1 + end + + function getter:peek2 () + local t1,v1,k1 = self:peek(1) + local t2,v2 = self:peek(k1+1) + return t1,v1,t2,v2 + end + + function getter:patch (idx,text) + out[idx] = text + end + + function getter:placeholder (put) + put:iden '/MARK?/' + return ii + end + + function getter:copy_from (pos,clear) + local res = {} + for i = pos, ii do + if out[i] and not out[i]:match '^#line' then + append(res,out[i]) + end + end + if clear then + for i = pos, ii do + table.remove(out,pos) + ii = ii - 1 + end + end + return table.concat(res) + end + + -- this feeds the results of a substitution into the token stream. + -- substitutions may be token lists, Lua strings or nil, in which case + -- the substitution is ignored. The result is to push a new token stream + -- onto the tstack, so it can be fetched using get() above + local function push_substitution (subst) + if subst == nil then return end + local st = type(subst) + push(tstack,tok) + if st == 'table' then + subst = scan_iter(subst) + elseif st == 'string' then + subst = scan_code(subst) + end + tok = subst + end + M.push_substitution = push_substitution + + -- a macro object consists of a subst object and (optional) parameters. + -- If there are parms, then a macro argument list must follow. + -- The subst object is either a token list or a function; if a token list we + -- substitute the actual parameters for the formal parameters; if a function + -- then we call it with the actual parameters. + -- Without parameters, it may be a simple substitution (TL or Lua string) or + -- may be a function. In the latter case we call it passing the token getter, + -- assuming that it will grab anything it needs from the token stream. + local function expand_macro(get,mac) + local pass_through + local subst = mac.subst + local fun = type(subst)=='function' + if mac.parms then + t = tnext(get); + if t ~= '(' then + M.error('macro '..mac.name..' expects parameters') + end + local args,err = Getter.list(get) + M.assert(args,'no end of argument list') + if fun then + subst = subst(unpack(args)) + else + if #mac.parms ~= #args then + M.error(mac.name.." takes "..#mac.parms.." arguments") + end + subst = substitute_tokenlist(subst,mac.parms,args) + end + elseif fun then + subst,pass_through = subst(getter,make_putter()) + end + push_substitution(subst) + return pass_through + end + + local multiline_tokens,sync = lexer.multiline_tokens,lexer.sync + local line,last_diff = 0,0 + + function do_action (action) + push_substitution(action(getter,make_putter())) + end + + if not subparse and keyword_handlers.BEGIN then + do_action(keyword_handlers.BEGIN) + end + + while t do + --print('tv',t,v) + local dump = true + if t == 'iden' then -- classic name macro + local mac = imacros[v] + if mac then + dump = expand_macro(get,mac) + end + elseif t == 'keyword' then + -- important to track block level for lexical scoping and block handlers + local class = keywords[v] + if class == 'open' then + if v ~= 'else' then level = level + 1 end + elseif class == 'close' then + level = level - 1 + if block_handlers[level] then + local res = process_block_handlers(level,get,v) + if res then push_substitution(res) end + end + --* elseif class == 'hook' then + end + local action = keyword_handlers[v] + if action then do_action(action) end + else -- any unused 'operator' token (like @, \, #) can be used as a macro + if use_c then + if v == '{' then + level = level + 1 + elseif v == '}' then + level = level - 1 + if block_handlers[level] then + local res = process_block_handlers(level,get,v) + if res then push_substitution(res) end + end + end + end + local mac = smacros[v] + if mac then + dump = expand_macro(get,mac) + end + end + if dump then + if multiline_tokens[t] then -- track output line + line = sync(line, v) + end + if iline_changed then + local diff = line - iline + if diff ~= last_diff then + local ldir = line_updater(iline,line,last_t,last_v) + if ldir then out[ii] = ldir; ii=ii+1 end + last_diff = diff + end + iline_changed = nil + end + out[ii] = v + ii = ii + 1 + end + t,v = get() + end + + return out,line_table +end + +--- take some Lua source and return the result of the substitution. +-- Does not raise any errors. +-- @param src either a string or a readable file object +-- @param name optional name for the chunk +-- @return the result or nil +-- @return the error, if error +function M.substitute_tostring(src,name,use_c,throw) + M.please_throw = true + local ok,out,li + if throw then + out,li = M.substitute(src,name,use_c) + else + ok,out,li = pcall(M.substitute,src,name,use_c) + end + if type(src) ~= 'string' and src.close then src:close() end + if not ok then return nil, out + else + return table.concat(out), li + end +end + +local lua52 = _VERSION:match '5.2' +local load, searchpath = load, package.searchpath + +if not lua52 then -- Lua 5.1 + function load (env,src,name) + local chunk,err = loadstring(src,name) + if chunk and env then + setfenv(chunk,env) + end + return chunk,err + end +end + +if not searchpath then + local sep = package.config:sub(1,1) + searchpath = function (mod,path) + mod = mod:gsub('%.',sep) + for m in path:gmatch('[^;]+') do + local nm = m:gsub('?',mod) + local f = io.open(nm,'r') + if f then f:close(); return nm end + end + end +end + +--- load Lua code in a given envrionment after passing +-- through the macro preprocessor. +-- @param src either a string or a readable file object +-- @param name optional name for the chunk +-- @param env the environment (may be nil) +-- @return the cnunk, or nil +-- @return the error, if no chunk +function M.load(src,name,env) + local res,err = M.substitute_tostring(src,'tmp') + if not res then return nil,err end + return loadin(env,res,name) +end + +--- evaluate Lua macro code in a given environment. +-- @param src either a string or a readable file object +-- @param env the environment (can be nil) +-- @return true if succeeded +-- @return result(s) +function M.eval(src,env) + local chunk,err = M.loadin(src,'(tmp)',env) + if not chunk then return nil,err end + return pcall(chunk) +end + +package.mpath = './?.m.lua' + +--- Make `require` use macro expansion. +-- This is controlled by package.mpath, which is initially './?.m.lua' +function M.set_package_loader() + -- directly inspired by https://github.com/bartbes/Meta/blob/master/meta.lua#L32, + -- after a suggestion by Alexander Gladysh + table.insert(package.loaders, function(name) + local fname = searchpath(name,package.mpath) + if not fname then return nil,"cannot find "..name end + local res,err = M.load(io.open(fname),lname) + if not res then + error (err) + end + return res + end) +end + +return macro diff --git a/tools/LuaMacro/macro/Getter.lua b/tools/LuaMacro/macro/Getter.lua new file mode 100644 index 0000000..af58b3c --- /dev/null +++ b/tools/LuaMacro/macro/Getter.lua @@ -0,0 +1,320 @@ +--- Getter class. Used to get values from the token stream. The first +-- argument `get` of a macro substitution function is of this type. +-- +-- M.define ('\\',function(get,put) +-- local args, body = get:idens('('), get:list() +-- return put:keyword 'function' '(' : idens(args) ')' : +-- keyword 'return' : list(body) : space() : keyword 'end' +-- end) +-- +-- The second argument `put` is a `TokenList` object. +-- @see macro.TokenList +-- @module macro.Getter + +local TokenList = require 'macro.TokenList' +local append = table.insert +local setmetatable = setmetatable + +local Getter = { + __call = function(self) + return self.fun() + end +} +local M = Getter + +Getter.__index = Getter; + +local scan_iter + +function Getter.new (get) + return setmetatable({fun=get},Getter) +end + +function Getter.from_tl(tl) + return Getter.new(scan_iter(tl)) +end + +local Tok = { + __tostring = function(self) + return self[2] + end +} + +local function tok_new (t) + return setmetatable(t,Tok) +end + +-- create a token iterator out of a token list +function Getter.scan_iter (tlist) + local i,n = 1,#tlist + return function(k) + if k ~= nil then + k = i + k + if k < 1 or k > n then return nil end + return tlist[k] + end + local tv = tlist[i] + if tv == nil then return nil end + i = i + 1 + return tv[1],tv[2] + end +end + +scan_iter = Getter.scan_iter + +--- get the next non-whitespace token. +-- @return token type +-- @return token value +-- @function Getter.next +function Getter.next(get) + local t,v = get() + while t == 'space' or t == 'comment' do + t,v = get() + end + return t,v +end + +local TL,LTL = TokenList.new, TokenList.new_list + + +local function tappend (tl,t,val) + val = val or t + append(tl,{t,val}) +end + +--- get a balanced block. +-- Typically for grabbing up to an `end` keyword including any nested +-- `if`, `do` or `function` blocks with their own `end` keywords. +-- @param tok the token stream +-- @param begintokens set of tokens requiring their own nested *endtokens* +-- (default: `{['do']=true,['function']=true,['if']=true}`) +-- @param endtokens set of tokens ending a block (default:`{['end']=true}`) +-- @return list of tokens +-- @return block end token in form `{type,value}` +-- @usage +-- -- copy a balanced table constructor +-- get:expecting '{' +-- put '{':tokens (get:block ({['{']=true}, {['}']=true}) '}') +function Getter.block(tok,begintokens,endtokens) + begintokens = begintokens or {['do']=true,['function']=true,['if']=true} + endtokens = endtokens or {['end']=true} + local level = 1 -- used to count expected matching `endtokens` + local tl = TL() + local token,value + repeat + token,value = tok() + if not token then return nil,'unexpected end of block' end + if begintokens[value] then + level = level + 1 + elseif endtokens[value] then + level = level - 1 + end + if level > 0 then -- final end token is returned separately + tappend(tl,token,value) + end + until level == 0 + return tl,tok_new{token,value} +end + +--- get a delimited list of token lists. +-- Typically used for grabbing argument lists like ('hello',a+1,fred(c,d)); will count parens +-- so that the delimiter (usually a comma) is ignored inside sub-expressions. You must have +-- already read the start token of the list, e.g. open parentheses. It will eat the end token +-- and return the list of TLs, plus the end token. Based on similar code in Penlight's +-- `pl.lexer` module. +-- @param tok the token stream +-- @param endt the end token (default ')') +-- @param delim the delimiter (default ',') +-- @return list of token lists +-- @return end token in form {type,value} +function Getter.list(tok,endtoken,delim) + endtoken = endtoken or ')' + delim = delim or ',' + local parm_values = LTL() + local level = 1 -- used to count ( and ) + local tl = TL() + local is_end + if type(endtoken) == 'function' then + is_end = endtoken + elseif endtoken == '\n' then + is_end = function(t,val) + return t == 'space' and val:find '\n' + end + else + is_end = function (t) + return t == endtoken + end + end + local token,value = tok() + if is_end(token,value) then return parm_values end + if token == 'space' then + token,value = tok() + end + while true do + if not token then return nil,'unexpected end of list' end -- end of stream is an error! + if is_end(token,value) and level == 1 then + append(parm_values,tl) + break + elseif token == '(' then + level = level + 1 + tappend(tl,'(') + elseif token == ')' then + level = level - 1 + if level == 0 then -- finished with parm list + append(parm_values,tl) + break + else + tappend(tl,')') + end + elseif token == '{' then + level = level + 1 + tappend(tl,'{') + elseif token == '}' then + level = level - 1 + tappend(tl,'}') + elseif token == delim and level == 1 then + append(parm_values,tl) -- a new parm + tl = TL() + else + tappend(tl,token,value) + end + token,value=tok() + end + return parm_values,tok_new{token,value} +end + +function Getter.upto_keywords (k1,k2) + return function(t,v) + return t == 'keyword' and (v == k1 or v == k2) + end,'' +end + +local tnext = Getter.next + + +function Getter.upto(tok,k1,k2) + local endt = k1 + if type(k1) == 'string' and k1:match '^%a+$' then + endt = Getter.upto_keywords(k1,k2) + end + local ltl,tok = tok:list(endt,'') + M.assert(ltl ~= nil and #ltl > 0,'failed to grab tokens') + return ltl[1],tok +end + +function Getter.line(tok) + return tok:upto(function(t,v) + return (t=='space' and v:match '\n') or t == 'comment' + end) +end + + +local function prettyprint (t, v) + v = v:gsub ("\n", "\\n") + if t == "string" then + if #v > 16 then v = v:sub(1,12).."..."..v:sub(1,1) end + return t.." "..v + end + if #v > 16 then v = v:sub(1,12).."..." end + if t == "space" or t == "comment" or t == "keyword" then + return t.." '"..v.."'" + elseif t == v then + return "'"..v.."'" + else + return t.." "..v + end +end + +--- get the next identifier token. +-- (will be an error if the token has wrong type) +-- @return identifier name +function Getter.iden(tok) + local t,v = tnext(tok) + M.assert(t == 'iden','expecting identifier, got '..prettyprint(t,v)) + return v +end + +Getter.name = Getter.iden -- backwards compatibility! + +--- get the next number token. +-- (will be an error if the token has wrong type) +-- @return converted number +function Getter.number(tok) + local t,v = tnext(tok) + M.assert(t == 'number','expecting number, got '..prettyprint(t,v)) + return tonumber(v) +end + +--- get a delimited list of identifiers. +-- works like list. +-- @param tok the token stream +-- @param endt the end token (default ')') +-- @param delim the delimiter (default ',') +-- @see list +function Getter.idens(tok,endt,delim) + local ltl,err = tok:list(endt,delim) + if not ltl then error('idens: '..err) end + local names = {} + -- list() will return {{}} for an empty list of tlists + for i = 1,#ltl do + local tl = ltl[i] + local tv = tl[1] + if tv then + if tv[1] == 'space' then tv = tl[2] end + names[i] = tv[2] + end + end + return names, err +end + +Getter.names = Getter.idens -- backwards compatibility! + +--- get the next string token. +-- (will be an error if the token has wrong type) +-- @return string value (without quotes) +function Getter.string(tok) + local t,v = tok:expecting("string") + return v:sub(2,-2) +end + +--- assert that the next token has the given type. This will throw an +-- error if the next non-whitespace token does not match. +-- @param type a token type ('iden','string',etc) +-- @param value a token value (optional) +-- @usage get:expecting '(' +-- @usage get:expecting ('iden','bonzo') +function Getter.expecting (tok,type,value) + local t,v = tnext(tok) + if t ~= type then M.error ("expected "..type.." got "..prettyprint(t,v)) end + if value then + if v ~= value then M.error("expected "..value.." got "..prettyprint(t,v)) end + end + return t,v +end + +--- peek ahead or before in the token stream. +-- @param k positive delta for looking ahead, negative for looking behind. +-- @param dont_skip true if you want to check for whitespace +-- @return the token type +-- @return the token value +-- @return the token offset +-- @function Getter.peek + +--- peek ahead two tokens. +-- @return first token type +-- @return first token value +-- @return second token type +-- @return second token value +-- @function Getter.peek2 + +--- patch the token stream at the end. +-- @param idx index in output table +-- @param text to replace value at that index +-- @function Getter.patch + +--- put out a placeholder for later patching. +-- @param put a putter object +-- @return an index into the output table +-- @function Getter.placeholder + +return Getter diff --git a/tools/LuaMacro/macro/TokenList.lua b/tools/LuaMacro/macro/TokenList.lua new file mode 100644 index 0000000..a18ac67 --- /dev/null +++ b/tools/LuaMacro/macro/TokenList.lua @@ -0,0 +1,201 @@ +--------------- +-- A TokenList class for generating token lists. +-- +-- There are also useful `get_` methods for extracting values from +-- the first token. +-- +-- @module macro.TokenList + +local TokenList = {} +local M = TokenList +TokenList.__index = TokenList + +local append = table.insert + +function TokenList.new (tl) + return setmetatable(tl or {},TokenList) +end + +local TokenListList = {} + +function TokenList.new_list (ltl) + return setmetatable(ltl or {},TokenListList) +end + +TokenListList.__index = function(self,key) + local m = TokenList[key] + return function(self,...) + local res = {} + for i = 1,#self do res[i] = m(self[i],...) end + return TokenList.new_list(res) + end +end + +-- token-getting helpers + + +local function extract (tl) + local tk = tl[1] + if tk[1] == 'space' then + tk = tl[2] + end + return tk +end + +--- get an identifier from front of a token list. +-- @return identifier name +function TokenList.get_iden (tl) + local tk = extract(tl) + M.assert(tk[1]=='iden','expecting identifier') + return tk[2] +end + +--- get an number from front of a token list. +-- @return number +function TokenList.get_number(tl) + local tk = extract(tl) + M.assert(tk[1]=='number','expecting number') + return tonumber(tk[2]) +end + +--- get a string from front of a token list. +-- @return string value (without quotes) +function TokenList.get_string(tl) + local tk = extract(tl) + M.assert(tk[1]=='string') + return tk[2]:sub(2,-2) -- watch out! what about long string literals?? +end + +--- takes a token list and strips spaces and comments. +-- @return new tokenlist +function TokenList.strip_spaces (tl) + local out = TokenList.new() + for _,t in ipairs(tl) do + if t[1] ~= 'comment' and t[1] ~= 'space' then + append(out,t) + end + end + return out +end + +--- pick the n-th token from this tokenlist. +-- Note that it returns the value and type, not the type and value. +-- @param n (1 to #self) +-- @return token value +-- @return token type +function TokenList.pick (tl,n) + local t = tl[n] + return t[2],t[1] +end + +-- token-putting helpers +local comma,space = {',',','},{'space',' '} + +--- append an identifier. +-- @param name the identifier +-- @param no_space true if you don't want a space after the iden +-- @return self +function TokenList.iden(res,name,no_space) + append(res,{'iden',name}) + if not no_space then + append(res,space) + end + return res +end + +TokenList.name = TokenList.iden -- backwards compatibility! + +--- append a string. +-- @param s the string +-- @return self +function TokenList.string(res,s) + append(res,{'string','"'..s..'"'}) + return res +end + +--- append a number. +-- @param val the number +-- @return self +function TokenList.number(res,val) + append(res,{'number',val}) + return res +end + +--- put out a list of identifiers, separated by commas. +-- @param res output token list +-- @param names a list of identifiers +-- @return self +function TokenList.idens(res,names) + for i = 1,#names do + res:iden(names[i],true) + if i ~= #names then append(res,comma) end + end + return res +end + +TokenList.names = TokenList.idens -- backwards compatibility! + +--- put out a token list. +-- @param res output token list +-- @param tl a token list +-- @return self +function TokenList.tokens(res,tl) + for j = 1,#tl do + append(res,tl[j]) + end + return res +end + +--- put out a list of token lists, separated by commas. +-- @param res output token list +-- @param ltl a list of token lists +-- @return self +function TokenList.list(res,ltl) + for i = 1,#ltl do + res:tokens(ltl[i]) + if i ~= #ltl then append(res,comma) end + end + return res +end + +--- put out a space token. +-- @param res output token list +-- @param space a string containing only whitespace (default ' ') +-- @return self +function TokenList.space(res,space) + append(res,{'space',space or ' '}) + return res +end + +--- put out a keyword token. +-- @param res output token list +-- @param keyw a Lua keyword +-- @param no_space true if you don't want a space after the iden +-- @return self +function TokenList.keyword(res,keyw,no_space) + append(res,{'keyword',keyw}) + if not no_space then + append(res,space) + end + return res +end + +--- convert this tokenlist into a string. +function TokenList.__tostring(tl) + local res = {} + for j = 1,#tl do + append(res,tl[j][2]) + end + return table.concat(res) +end + +--- put out a operator token. This is the overloaded call operator +-- for token lists. +-- @param res output token list +-- @param keyw an operator string +function TokenList.__call(res,t,v) + append(res,{t,v or t}) + return res +end + +return TokenList diff --git a/tools/LuaMacro/macro/all.lua b/tools/LuaMacro/macro/all.lua new file mode 100644 index 0000000..0d7c098 --- /dev/null +++ b/tools/LuaMacro/macro/all.lua @@ -0,0 +1,5 @@ +require 'macro.forall' +require 'macro.lambda' +require 'macro.try' +require 'macro.do' + diff --git a/tools/LuaMacro/macro/assert.lua b/tools/LuaMacro/macro/assert.lua new file mode 100644 index 0000000..b25daaf --- /dev/null +++ b/tools/LuaMacro/macro/assert.lua @@ -0,0 +1,74 @@ +--- a simple testing framework. +-- Defines a single statment macro assert_ which has the following syntax: +-- +-- - assert_ val1 == val2 +-- - assert_ val1 > val2 +-- - assert_ val1 < val2 +-- - assert_ val1 matches val2 (using string matching) +-- - assert_ val1 throws val2 (ditto, on exception string) +-- +-- The `==` case has some special forms. If `val2` is `(v1,v2,..)` then +-- it's assumed that the expression `val1` returns multiple values. `==` will +-- also do value equality for plain tables. If `val2` is a number given in +-- %f format (such as 3.14) then it will match `vall` up to that specified +-- number of digits. +-- +-- assert_ {one=1,two=2} == {two=2,one=1} +-- assert_ 'hello' matches '^hell' +-- assert_ 2 > 1 +-- assert_ ('hello'):find 'll' == (3,4) +-- assert_ a.x throws 'attempt to index global' +-- @module macro.assert + +local M = require 'macro' +local relop = { + ['=='] = 'eq', + ['<'] = 'lt', + ['>'] = 'gt' +} + +local function numfmt (x) + local int,frac = x:match('(%d+)%.(%d+)') + if not frac then return nil end + return '%'..#x..'.'..#frac..'f', x +end + +--- assert that two values match the desired relation. +-- @macro assert_ +M.define('assert_',function(get,put) + local testx,tok = get:upto(function(t,v) + return relop[t] or (t == 'iden' and (v == 'matches' or v == 'throws')) + end) + local testy,eos = get:line() + local otesty = testy + testx = tostring(testx) + testy = tostring(testy) + local t,v,op = tok[1],tok[2] + if relop[t] then + op = relop[t] + if t == '==' then + if testy:match '^%(.+%)$' then + testx = 'T_.tuple('..testx..')' + testy = 'T_.tuple'..testy + elseif #otesty == 1 and otesty[1][1] == 'number' then + local num = otesty[1][2] + local fmt,num = numfmt(num) + if fmt then -- explicit floating-point literal + testy = '"'..num..'"' + testx = '("'..fmt..'"):format('..testx..')' + op = 'match' + end + end + end + elseif v == 'matches' then + op = 'match' + elseif v == 'throws' then + op = 'match' + testx = 'T_.pcall_no(function() return '..testx..' end)' + end + return ('T_.assert_%s(%s,%s)%s'):format(op,testx,testy,tostring(eos)) +end) + +return function() + return "T_ = require 'macro.lib.test'" +end diff --git a/tools/LuaMacro/macro/builtin.lua b/tools/LuaMacro/macro/builtin.lua new file mode 100644 index 0000000..12c8b38 --- /dev/null +++ b/tools/LuaMacro/macro/builtin.lua @@ -0,0 +1,161 @@ +------- +-- LuaMacro built-in macros. +-- @module macro.builtin + +local M = require 'macro' + +local function macro_def (scoped) + return function (get) + local t,name,parms,openp + local t,name = get:next() + local upto,ret + if t == '(' then + t,name = get:next() + upto = function(t,v) return t == ')' end + else + upto = function(t,v) + return t == 'space' and v:find '\n' + end + -- return space following (returned by copy_tokens) + ret = true + end + -- might be immediately followed by a parm list + t,openp = get() + if openp == '(' then + parms = get:names() + end + -- the actual substitution is up to the end of the line + local args, space = M.copy_tokens(get,upto) + if scoped then + M.define_scoped(name,args,parms) + else + M.set_macro(name,args,parms) + end + return ret and space[2] + end +end + +--- a macro for defining lexically scoped simple macros. +-- def_ may be followed by an arglist, and the substitution is the +-- rest of the line. +-- @usage def_ block (function() _END_CLOSE_ +-- @usage def_ sqr(x) ((x)*(x)) +-- @macro def_ +M.define ('def_',macro_def(true)) + +--- a global version of `def_`. +-- @see def_ +-- @macro define_ +M.define ('define_',macro_def(false)) + +--- set the value of an existing macro. +-- the name and value follows immediately, and the value must be +-- a single token +-- @usage set_ T 'string' +-- @usage set_ F function +-- @macro set_ +M.define('set_',function(get) + local name = get:name() + local t,v = get:next() + M.set_macro(name,{{t,v}}) +end) + +--- undefining identifier macros. +-- @macro undef_ +M.define('undef_',function(get) + M.set_macro(get:name()) +end) + +--- Insert text after current block end. `_END_` is followed by a quoted string +-- and is used to insert that string after the current block closes. +-- @macro _END_ +M.define ('_END_',function(get) + local str = get:string() + M.block_handler(-1,function(get,word) + if word ~= 'end' then return nil,true end + return str + end) +end) + +--- insert an end after the next closing block. +-- @macro _END_END_ +-- @see _END_ +M.define '_END_END_ _END_ " end"' + +--- insert a closing parens after next closing block. +-- @usage def_ begin (function() _END_CLOSE_ +-- fun begin ... end --> fun (function() ... end) +-- @macro _END_CLOSE_ +-- @see _END_ +M.define '_END_CLOSE_ _END_ ")"' + +--- 'stringizing' macro. +-- Will convert its argument into a string. +-- @usage def_ _assert(x) assert(x,_STR_(x)) +-- @macro _STR_ +M.define('_STR_(x)',function(x) + x = tostring(x) + local put = M.Putter() + return put '"':name(x) '"' +end) + +-- macro stack manipulation + + +--- push a value onto a given macro' stack. +-- @macro _PUSH_ +-- @param mac existing macro name +-- @param V a string +M.define('_PUSH_(mac,V)',function(mac,V) + M.push_macro_stack(mac:get_string(),V:get_string()) +end) + +--- pop a value from a macro's stack. +-- @macro _POP_ +-- @param mac existing macro name +-- @return a string +-- @see _PUSH_ +M.define('_POP_',function(get,put) + local val = M.pop_macro_stack(get:string()) + if val then + return put(val) + end +end) + +--- drop the top of a macro's stack. +-- Like `_POP_`, except that it does not return the value +-- @macro _DROP_ +-- @return existing macro name +-- @see _POP_ +M.define('_DROP_',function(get) + M.pop_macro_stack(get:string()) +end) + +--- Load a Lua module immediately. This allows macro definitions to +-- to be loaded before the rest of the file is parsed. +-- If the module returns a function, then this is assumed to be a +-- substitution function, allowing macro modules to insert code +-- at this point. +-- @macro require_ +M.define('require_',function(get,put) + local name = get:string() + local ok,fn = pcall(require,name) + if not ok then + fn = require('macro.'..name) + end + if type(fn) == 'function' then + return fn(get,put) + end +end) + +--- Include the contents of a file. This inserts the file directly +-- into the token stream, and is equivalent to cpp's `#include` directive. +-- @macro include_ +M.define('include_',function(get) + local str = get:string() + local f = M.assert(io.open(str)) + local txt = f:read '*a' + f:close() + M.push_substitution(txt) +end) + diff --git a/tools/LuaMacro/macro/clexer.lua b/tools/LuaMacro/macro/clexer.lua new file mode 100644 index 0000000..fd859a8 --- /dev/null +++ b/tools/LuaMacro/macro/clexer.lua @@ -0,0 +1,169 @@ +--[[--- A C lexical scanner using LPeg. += CREDITS += based on the C lexer in Peter Odding's lua-lxsh +@module macro.clexer +--]] + +local clexer = {} +local lpeg = require 'lpeg' +local P, R, S, C, Cc, Ct = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Ct + +-- create a pattern which captures the lua value [id] and the input matching +-- [patt] in a table +local function token(id, patt) return Ct(Cc(id) * C(patt)) end + +-- private interface +local table_of_tokens +local extra_tokens + +function clexer.add_extra_tokens(extra) + extra_tokens = extra_tokens or {} + for _,t in ipairs(extra) do + table.insert(extra_tokens,t) + end + table_of_tokens = nil -- re-initialize +end + +function clexer.init () + local digit = R('09') + + local upp, low = R'AZ', R'az' + local oct, dec = R'07', R'09' + local hex = dec + R'AF' + R'af' + local letter = upp + low + local alnum = letter + dec + '_' + local endline = S'\r\n\f' + local newline = '\r\n' + endline + local escape = '\\' * ( newline + + S'\\"\'?abfnrtv' + + (#oct * oct^-3) + + ('x' * #hex * hex^-2)) + + + -- range of valid characters after first character of identifier + local idsafe = R('AZ', 'az', '\127\255') + P '_' + + -- operators + local OT = P '==' + if extra_tokens then + for _,ex in ipairs(extra_tokens) do + OT = OT + P(ex) + end + end + local operator = token('operator', OT + P '.' + P'>>=' + '<<=' + '--' + '>>' + '>=' + '/=' + '==' + '<=' + + '+=' + '<<' + '*=' + '++' + '&&' + '|=' + '||' + '!=' + '&=' + '-=' + + '^=' + '%=' + '->' + S',)*%+&(-~/^]{}|.[>!?:=<;') + -- identifiers + local ident = token('iden', idsafe * (idsafe + digit) ^ 0) + + -- keywords + local keyword = token('keyword', (P 'auto' + P 'break' + P 'case' + P'char' + + P 'const' + P 'continue' + P 'default' + + P 'do' + P 'double' + P 'else' + P 'enum' + P 'extern' + P 'float' + + P 'for' + P 'goto' + P 'if' + P 'int' + P 'long' + P 'register' + + P 'return' + P 'short' + P 'signed' + P 'sizeof' + P 'static' + + P 'struct' + P 'switch' + P 'typedef' + P 'union' + P 'void' + + P 'volatile' + P 'while') * -(idsafe + digit)) + + -- numbers + local number_sign = S'+-'^-1 + local number_decimal = digit ^ 1 + local number_hexadecimal = P '0' * S 'xX' * R('09', 'AF', 'af') ^ 1 + local number_float = (digit^1 * P'.' * digit^0 + P'.' * digit^1) * + (S'eE' * number_sign * digit^1)^-1 + local number = token('number', number_hexadecimal + + number_float + + number_decimal) + + + local string = token('string', '"' * ((1 - S'\\\r\n\f"') + escape)^0 * '"') + local char = token('char',"'" * ((1 - S"\\\r\n\f'") + escape) * "'") + + -- comments + local singleline_comment = P '//' * (1 - S '\r\n\f') ^ 0 + local multiline_comment = '/*' * (1 - P'*/')^0 * '*/' + local comment = token('comment', multiline_comment + singleline_comment) + local prepro = token('prepro',P '#' * (1 - S '\r\n\f') ^ 0) + + -- whitespace + local whitespace = token('space', S('\r\n\f\t ')^1) + + -- ordered choice of all tokens and last-resort error which consumes one character + local any_token = whitespace + number + keyword + ident + + string + char + comment + prepro + operator + token('error', 1) + + + table_of_tokens = Ct(any_token ^ 0) +end + +-- increment [line] by the number of line-ends in [text] +local function sync(line, text) + local index, limit = 1, #text + while index <= limit do + local start, stop = text:find('\r\n', index, true) + if not start then + start, stop = text:find('[\r\n\f]', index) + if not start then break end + end + index = stop + 1 + line = line + 1 + end + return line +end +clexer.sync = sync + +clexer.line = 0 + +-- we only need to synchronize the line-counter for these token types +local multiline_tokens = { comment = true, space = true } +clexer.multiline_tokens = multiline_tokens + +function clexer.scan_c_tokenlist(input) + if not table_of_tokens then + clexer.init() + end + assert(type(input) == 'string', 'bad argument #1 (expected string)') + local line = 1 + local tokens = lpeg.match(table_of_tokens, input) + for i, token in pairs(tokens) do + local t = token[1] + if t == 'operator' or t == 'error' then + token[1] = token[2] + end + token[3] = line + if multiline_tokens[t] then + line = sync(line, token[2]) + end + end + return tokens +end + +--- get a token iterator from a source containing Lua code. +-- S is the source - can be a string or a file-like object (i.e. read() returns line) +-- Note that this token iterator includes spaces and comments, and does not convert +-- string and number tokens - so e.g. a string token is quoted and a number token is +-- an unconverted string. +function clexer.scan_c(input,name) + if type(input) ~= 'string' and input.read then + input = input:read('*a') + end + local tokens = clexer.scan_c_tokenlist(input) + local i, n = 1, #tokens + return function(k) + if k ~= nil then + k = i + k + if k < 1 or k > n then return nil end + return tokens[k] + end + local tok = tokens[i] + i = i + 1 + if tok then + clexer.line = tok[3] + clexer.name = name + return tok[1],tok[2] + end + end + +end + +return clexer diff --git a/tools/LuaMacro/macro/do.lua b/tools/LuaMacro/macro/do.lua new file mode 100644 index 0000000..45cf84c --- /dev/null +++ b/tools/LuaMacro/macro/do.lua @@ -0,0 +1,75 @@ +--- An intelligent 'loop-unrolling' macro. +-- `do_` defines a named scoped macro `var` which is the loop iterator. +-- +-- For example, +-- +-- y = 0 +-- do_(i,1,10 +-- y = y + i +-- ) +-- assert(y == 55) +-- +-- `tuple` is an example of how the expansion of a macro can be +-- controlled by its context. Normally a tuple `A` expands to +-- `A_1,A_2,A_3` but inside `do_` it works element-wise: +-- +-- tuple(3) A,B +-- def_ do3(stmt) do_(k,1,3,stmt) +-- do3(A = B/2) +-- +-- This expands as +-- +-- A_1 = B_1/2 +-- A_2 = B_2/2 +-- A_3 = B_3/2 +-- +-- @module macro.do +local M = require 'macro' + +--- Expand a loop inline. +-- @p var the loop variable +-- @p start initial value of `var` +-- @p finish final value of `var` +-- @p stat the statement containing `var` +-- @macro do_ +M.define('do_(v,s,f,stat)',function(var,start,finish,statements) + -- macros with specified formal args have to make their own putter, + -- and convert the actual arguments to the type they expect. + local put = M.Putter() + var,start,finish = var:get_iden(),start:get_number(),finish:get_number() + M.push_macro_stack('do_',var) + -- 'do_' works by setting the variable macro for each value + for i = start, finish do + put:name 'set_':name(var):number(i):space() + put:tokens(statements) + end + put:name 'undef_':name(var) + put:name '_DROP_':string 'do_':space() + return put +end) + +--- an example of conditional expansion. +-- `tuple` takes a list of variable names, like a declaration list except that it +-- must end with a line end. +-- @macro tuple +M.define('tuple',function(get) + get:expecting '(' + local N = get:number() + get:expecting ')' + local names = get:names '\n' + for _,name in ipairs(names) do + M.define(name,function(get,put) + local loop_var = M.value_of_macro_stack 'do_' + if loop_var then + local loop_idx = tonumber(M.get_macro_value(loop_var)) + return put:name (name..'_'..loop_idx) + else + local out = {} + for i = 1,N do + out[i] = name..'_'..i + end + return put:names(out) + end + end) + end +end) diff --git a/tools/LuaMacro/macro/forall.lua b/tools/LuaMacro/macro/forall.lua new file mode 100644 index 0000000..8ec5c68 --- /dev/null +++ b/tools/LuaMacro/macro/forall.lua @@ -0,0 +1,70 @@ +-------------------- +-- `forall` statement. +-- The syntax is `forall VAR SELECT [if CONDN] do` where +-- `SELECT` is either `in TBL` or `= START,FINISH` +-- +-- For example, +-- +-- forall name in {'one','two'} do print(name) end +-- +-- forall obj in get_objects() if obj:alive() then +-- obj:action() +-- end +-- +-- Using `forall`, we also define _list comprehensions_ like +-- `L{s:upper() | s in names if s:match '%S+'}` +-- +-- @module macro.forall + +local M = require 'macro' + +--- extended for statement. +-- @macro forall +M.define('forall',function(get,put) + local var = get:iden() + local t,v = get:next() + local rest,endt = get:list(M.upto_keywords('do','if')) + put:keyword 'for' + if v == 'in' then + put:iden '_' ',' :iden(var):keyword 'in' + put:iden 'ipairs' '(' :list(rest) ')' + elseif v == '=' then + put:iden(var) '=' :list(rest) + else + M.error("expecting in or =") + end + put:keyword 'do' + if endt[2] == 'if' then + rest,endt = get:list(M.upto_keywords('do')) + put:keyword 'if':list(rest):keyword 'then':iden '_END_END_' + end + return put +end) + +--- list comprehension. +-- Syntax is `L{expr | select}` where `select` is as in `forall`, +-- or `L{expr for select}` where `select` is as in the regular `for` statement. +-- @macro L +-- @return a list of values +-- @usage L{2*x | x in {1,2,3}} == {1,4,9} +-- @usage L{2*x|x = 1,3} == {1,4,9} +-- @usage L{{k,v} for k,v in pairs(t)} +-- @see forall +M.define('L',function(get,put) + local t,v = get:next() -- must be '{' + local expr,endt = get:list(function(t,v) + return t == '|' or t == 'keyword' and v == 'for' + end,'') + local select = get:list('}','') + put '(' : keyword 'function' '(' ')' :keyword 'local':iden 'res' '=' '{' '}' + if endt[2] == '|' then + put:iden'forall' + else + put:keyword 'for' + end + put:list(select):space():keyword'do' + put:iden'res' '[' '#' :iden'res' '+' :number(1) ']' '=' :list(expr):space() + put:keyword 'end' :keyword 'return' : iden 'res' :keyword 'end' ')' '(' ')' + put:iden '_POP_':string'L' + return put +end) diff --git a/tools/LuaMacro/macro/ifelse.lua b/tools/LuaMacro/macro/ifelse.lua new file mode 100644 index 0000000..3d5a0df --- /dev/null +++ b/tools/LuaMacro/macro/ifelse.lua @@ -0,0 +1,90 @@ +local M = require 'macro' + +local function eval (expr,was_expr) + expr = tostring(expr) + if was_expr then expr = "return "..expr end + local chunk = M.assert(loadstring(expr)) + local ok, res = pcall(chunk) + if not ok then M.error("error evaluating "..res) end + return res +end + +local function eval_line (get,was_expr) + local args = get:line() + return eval(args,was_expr) +end + +local function grab (get) + local ilevel = 0 + while true do + local t,v = get() + while t ~= '@' do t = get() end + t,v = get() + if v == 'if' then + ilevel = ilevel + 1 + else -- 'end','elseif','else' + if ilevel > 0 and v == 'end' then + ilevel = ilevel - 1 + elseif ilevel == 0 then return '@'..v end + end + end +end + +M.define('@',function(get,put) + local t,v = get() +--~ print('got',t,v) + return put:iden(v..'_') +end) + +local ifstack,push,pop = {},table.insert,table.remove + +local function push_if (res) +--~ print 'push' + push(ifstack, not (res==false or res==nil)) +end + +local function pop_if () +--~ print 'pop' + pop(ifstack) +end + +M.define('if_',function(get) + local res = eval_line(get,true) + push_if(res) + if not res then + return grab(get) + end +end) + +M.define('elseif_',function(get) + local res + if ifstack[#ifstack] then + res = false + else + res = eval_line(get,true) + pop_if() + push_if(res) + end + if not res then + return grab(get) + end +end) + +M.define('else_',function(get) + if #ifstack == 0 then M.error("mismatched else") end + if ifstack[#ifstack] then + return grab(get) + end +end) + +M.define('end_',function(get) + pop_if() +end) + +M.define('let_',function(get) + eval_line(get) +end) + +M.define('eval_(X)',function(X) + return tostring(eval(X,true)) +end) diff --git a/tools/LuaMacro/macro/lambda.lua b/tools/LuaMacro/macro/lambda.lua new file mode 100644 index 0000000..677f997 --- /dev/null +++ b/tools/LuaMacro/macro/lambda.lua @@ -0,0 +1,22 @@ +--- Short anonymous functions (lambdas). +-- This syntax is suited +-- to any naive token-processor because the payload is always inside parens. +-- It is an example of a macro associated with a 'operator' character. +-- +-- Syntax is `\()` +-- +-- `\x(x+10)` is short for +-- `function(x) return x+10 end`. There may be a number of formal argumets, +-- e.g. `\x,y(x+y)` or there may be none, e.g. `\(somefun())`. Such functions +-- may return multiple values, e.g `\x(x+1,x-1)`. +-- +-- @module macro.lambda + +local M = require 'macro' + +M.define ('\\',function(get,put) + local args, body = get:idens('('), get:list() + return put:keyword 'function' '(' : idens(args) ')' : + keyword 'return' : list(body) : space() : keyword 'end' +end) + diff --git a/tools/LuaMacro/macro/lc.lua b/tools/LuaMacro/macro/lc.lua new file mode 100644 index 0000000..0d2968d --- /dev/null +++ b/tools/LuaMacro/macro/lc.lua @@ -0,0 +1,343 @@ +-- Simplifying writing C extensions for Lua +-- Adds new module and class constructs; +-- see class1.lc and str.lc for examples. +local M = require 'macro' + +function dollar_subst(s,tbl) + return (s:gsub('%$%((%a+)%)',tbl)) +end + +-- reuse some machinery from the C-skin experiments +local push,pop = table.insert,table.remove +local bstack,btop = {},{} + +local function push_brace_stack (newv) + newv = newv or {} + newv.lev = 0 + push(bstack,btop) + btop = newv +end + +M.define('{',function() + if btop.lev then + btop.lev = btop.lev + 1 + end + return nil,true --> pass-through macro +end) + +M.define('}',function(get,put) + if not btop.lev then + return nil,true + elseif btop.lev == 0 then + local res + if btop.handler then res = btop.handler(get,put) end + if not res then res = put:space() '}' end + btop = pop(bstack) + return res + else + btop.lev = btop.lev - 1 + return nil,true --> pass-through macro + end +end) + +--------- actual implementation begins ------- + +local append = table.insert +local module + +local function register_functions (names,cnames) + local out = {} + for i = 1,#names do + append(out,(' {"%s",l_%s},'):format(names[i],cnames[i])) + end + return table.concat(out,'\n') +end + +local function finalizers (names) + local out = {} + for i = 1,#names do + append(out,names[i].."(L);") + end + return table.concat(out,'\n') +end + +local typedefs + +local preamble = [[ +#include +#include +#include +#ifdef WIN32 +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif +#if LUA_VERSION_NUM > 501 +#define lua_objlen lua_rawlen +#endif +]] + +local finis = [[ +static const luaL_Reg $(cname)_funs[] = { + $(funs) + {NULL,NULL} +}; + +EXPORT int luaopen_$(cname) (lua_State *L) { +#if LUA_VERSION_NUM > 501 + lua_newtable(L); + luaL_setfuncs (L,$(cname)_funs,0); + lua_pushvalue(L,-1); + lua_setglobal(L,"$(cname)"); +#else + luaL_register(L,"$(cname)",$(cname)_funs); +#endif + $(finalizers) + return 1; +} +]] + +M.define('module',function(get) + local name = get:string() + local cname = name:gsub('%.','_') + get:expecting '{' + local out = preamble .. typedefs + push_brace_stack{ + name = name, cname = cname, + names = {}, cnames = {}, finalizers = {}, + handler = function() + local out = {} + local funs = register_functions(btop.names,btop.cnames) + local final = finalizers(btop.finalizers) + append(out,dollar_subst(finis, { + cname = cname, + name = name, + funs = funs, + finalizers = final + })) + return table.concat(out,'\n') + end } + module = btop + return out +end) + + +M.define('def',function(get) + local fname = get:name() + local cname = (btop.ns and btop.ns..'_' or '')..fname + append(btop.names,fname) + append(btop.cnames,cname) + get:expecting '(' + local args = get:list():strip_spaces() + get:expecting '{' + local t,space = get() + indent = space:gsub('^%s*[\n\r]',''):gsub('%s$','') + local out = {"static int l_"..cname.."(lua_State *L) {"} + if btop.massage_arg then + btop.massage_arg(args) + end + for i,arg in ipairs(args) do + local mac = arg[1][2]..'_init' + if arg[3] and arg[3][1] == '=' then + mac = mac .. 'o' + i = i .. ',' .. arg[4][2] + end + if not arg[2] then M.error("parameter must be TYPE NAME [= VALUE]") end + append(out,indent..mac..'('..arg[2][2]..','..i..');') + end + --append(out,space) + return table.concat(out,'\n')..space +end) + +M.define('constants',function(get,put) + get:expecting '{' + local consts = get:list '}' :strip_spaces() + --for k,v in pairs(btop) do io.stderr:write(k,'=',tostring(v),'\n') end + -- os.exit() + local fname = 'set_'..btop.cname..'_constants' + local out = { 'static void '..fname..'(lua_State *L) {'} + if not btop.finalizers then M.error("not inside a module") end + append(btop.finalizers,fname) + for _,c in ipairs(consts) do + local type,value,name + if #c == 1 then -- a simple int constant: CONST + name = c:pick(1) + type = 'Int' + value = name + else -- Type CONST [ = VALUE ] + type = c:pick(1) + name = c:pick(2) + if #c == 2 then + value = name + else + value = c:pick(4) + end + end + append(out,('%s_set("%s",%s);'):format(type,name,value )) + end + append(out,'}') + return table.concat(out,'\n') +end) + +M.define('assign',function(get) + get:expecting '{' + local asses = get:list '}' :strip_spaces() + local out = {} + for _,c in ipairs(asses) do + append(out,('%s_set("%s",%s);\n'):format(c:pick(1),c:pick(2),c:pick(4)) ) + end + return table.concat(out,'\n') +end) + +local load_lua = [[ +static void load_lua_code (lua_State *L) { + luaL_dostring(L,lua_code_block); +} +]] + +M.define('lua',function(get) + get:expecting '{' + local block = tostring(get:upto '}') + local code_name = 'lua_code_block' + local out = {'static const char *'.. code_name .. ' = ""\\'} + for line in block:gmatch('([^\r\n]+)') do + line = line:gsub('\\','\\\\'):gsub('"','\\"') + append(out,' "'..line..'\\n"\\') + end + append(out,';') + append(out,load_lua) + out = table.concat(out,'\n') + append(module.finalizers,'load_lua_code') + return out +end) + +typedefs = [[ +typedef const char *Str; +typedef const char *StrNil; +typedef int Int; +typedef double Number; +typedef int Boolean; +]] + + +M.define 'Str_init(var,idx) const char *var = luaL_checklstring(L,idx,NULL)' +M.define 'Str_inito(var,idx,val) const char *var = luaL_optlstring(L,idx,val,NULL)' +M.define 'Str_set(name,value) lua_pushstring(L,value); lua_setfield(L,-2,name)' +M.define 'Str_get(var,name) lua_getfield(L,-1,name); var=lua_tostring(L,-1); lua_pop(L,1)' +M.define 'Str_geti(var,idx) lua_rawgeti(L,-1,idx); var=lua_tostring(L,-1); lua_pop(L,1)' + +M.define 'StrNil_init(var,idx) const char *var = lua_tostring(L,idx)' + +M.define 'Int_init(var,idx) int var = luaL_checkinteger(L,idx)' +M.define 'Int_inito(var,idx,val) int var = luaL_optinteger(L,idx,val)' +M.define 'Int_set(name,value) lua_pushinteger(L,value); lua_setfield(L,-2,name)' +M.define 'Int_get(var,name) lua_getfield(L,-1,name); var=lua_tointeger(L,-1); lua_pop(L,1)' +M.define 'Int_geti(var,idx) lua_rawgeti(L,-1,idx); var=lua_tointeger(L,-1); lua_pop(L,1)' + +M.define 'Number_init(var,idx) double var = luaL_checknumber(L,idx)' +M.define 'Number_inito(var,idx,val) double var = luaL_optnumber(L,idx,val)' +M.define 'NUmber_set(name,value) lua_pushnumber(L,value); lua_setfield(L,-2,name)' +M.define 'Number_get(var,name) lua_getfield(L,-1,name); var=lua_tonumber(L,-1); lua_pop(L,1)' +M.define 'Number_geti(var,idx) lua_rawgeti(L,-1,idx); var=lua_tonumber(L,-1); lua_pop(L,1)' + +M.define 'Boolean_init(var,idx) int var = lua_toboolean(L,idx)' +M.define 'Boolean_set(name,value) lua_pushboolean(L,value); lua_setfield(L,-2,name)' +M.define 'Boolean_get(var,name) lua_getfield(L,-1,name); var=lua_toboolean(L,-1); lua_pop(L,1)' +M.define 'Boolean_geti(var,idx) lua_rawgeti(L,-1,idx); var=lua_toboolean(L,-1); lua_pop(L,1)' + +M.define 'Value_init(var,idx) int var = idx' + +M.define('lua_tests',function(get) + get:expecting '{' + local body = get:upto '}' + local f = io.open(M.filename..'.lua','w') + f:write(tostring(body)) + f:close() +end) + +------ class support ---------------------- + +local klass_ctor = "static void $(klass)_ctor(lua_State *L, $(klass) *this, $(fargs))" + +local begin_klass = [[ + +typedef struct { + $(fields) +} $(klass); + +define_ $(klass)_init(var,idx) $(klass) *var = $(klass)_arg(L,idx) + +#define $(klass)_MT "$(klass)" + +$(klass) * $(klass)_arg(lua_State *L,int idx) { + $(klass) *this = ($(klass) *)luaL_checkudata(L,idx,$(klass)_MT); + luaL_argcheck(L, this != NULL, idx, "$(klass) expected"); + return this; +} + +$(ctor); + +static int push_new_$(klass)(lua_State *L,$(fargs)) { + $(klass) *this = ($(klass) *)lua_newuserdata(L,sizeof($(klass))); + luaL_getmetatable(L,$(klass)_MT); + lua_setmetatable(L,-2); + $(klass)_ctor(L,this,$(aargs)); + return 1; +} + +]] + +local end_klass = [[ + +static const struct luaL_Reg $(klass)_methods [] = { + $(methods) + {NULL, NULL} /* sentinel */ +}; + +static void $(klass)_register (lua_State *L) { + luaL_newmetatable(L,$(klass)_MT); +#if LUA_VERSION_NUM > 501 + luaL_setfuncs(L,$(klass)_methods,0); +#else + luaL_register(L,NULL,$(klass)_methods); +#endif + lua_pushvalue(L,-1); + lua_setfield(L,-2,"__index"); + lua_pop(L,1); +} +]] + +M.define('class',function(get) + local name = get:iden() + get:expecting '{' + local fields = get:upto (function(t,v) + return t == 'iden' and v == 'constructor' + end) + fields = tostring(fields):gsub('%s+$','\n') + get:expecting '(' + local out = {} + local args = get:list() + local f_args = args:strip_spaces() + local a_args = f_args:pick(2) + f_args = table.concat(args:__tostring(),',') + a_args = table.concat(a_args,',') + local subst = {klass=name,fields=fields,fargs=f_args,aargs=a_args } + local proto = dollar_subst(klass_ctor,subst) + subst.ctor = proto + append(out,dollar_subst(begin_klass,subst)) + append(out,proto) + local pp = {{'iden',name},{'iden','this'}} + push_brace_stack{ + names = {}, cnames = {}, ns = name, cname = name, + massage_arg = function(args) + table.insert(args,1,pp) + end, + handler = function(get,put) + append(module.finalizers,name.."_register") + local methods = register_functions(btop.names,btop.cnames) + return dollar_subst(end_klass,{methods=methods,klass=name,fargs=f_args,aargs=a_args}) + end + } + return table.concat(out,'\n') +end) + diff --git a/tools/LuaMacro/macro/lexer.lua b/tools/LuaMacro/macro/lexer.lua new file mode 100644 index 0000000..58ab53a --- /dev/null +++ b/tools/LuaMacro/macro/lexer.lua @@ -0,0 +1,179 @@ +--[[--- A Lua lexical scanner using LPeg. += CREDITS +Written by Peter Odding, 2007/04/04 + += THANKS TO +- the Lua authors for a wonderful language; +- Roberto for LPeg; +- caffeine for keeping me awake :) + += LICENSE +Shamelessly ripped from the SQLite[3] project: + + The author disclaims copyright to this source code. In place of a legal + notice, here is a blessing: + + May you do good and not evil. + May you find forgiveness for yourself and forgive others. + May you share freely, never taking more than you give. + +@module macro.lexer +--]] + +local lexer = {} +local lpeg = require 'lpeg' +local P, R, S, C, Cb, Cc, Cg, Cmt, Ct = + lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cb, lpeg.Cc, lpeg.Cg, lpeg.Cmt, lpeg.Ct + +-- create a pattern which captures the lua value [id] and the input matching +-- [patt] in a table +local function token(id, patt) return Ct(Cc(id) * C(patt)) end + +-- private interface +local table_of_tokens +local extra_tokens + +function lexer.add_extra_tokens(extra) + extra_tokens = extra_tokens or {} + for _,t in ipairs(extra) do + table.insert(extra_tokens,t) + end + table_of_tokens = nil -- re-initialize +end + +function lexer.init () + local digit = R('09') + + -- range of valid characters after first character of identifier + --local idsafe = R('AZ', 'az', '\127\255') + P '_' + local idsafe = R('AZ', 'az') + P '_' + R '\206\223' * R '\128\255' + -- operators + local OT = P '==' + if extra_tokens then + for _,ex in ipairs(extra_tokens) do + OT = OT + P(ex) + end + end + local operator = token('operator', OT + P '.' + P '~=' + P '<=' + P '>=' + P '...' + + P '..' + S '+-*/%^#=<>;:,.{}[]()') + -- identifiers + local ident = token('iden', idsafe * (idsafe + digit) ^ 0) + + -- keywords + local keyword = token('keyword', (P 'and' + P 'break' + P 'do' + P 'elseif' + + P 'else' + P 'end' + P 'false' + P 'for' + P 'function' + P 'if' + + P 'in' + P 'local' + P 'nil' + P 'not' + P 'or' + P 'repeat' + P 'return' + + P 'then' + P 'true' + P 'until' + P 'while') * -(idsafe + digit)) + + -- numbers + local number_sign = S'+-'^-1 + local number_decimal = digit ^ 1 + local number_hexadecimal = P '0' * S 'xX' * R('09', 'AF', 'af') ^ 1 + local number_float = (digit^1 * P'.' * digit^0 + P'.' * digit^1) * + (S'eE' * number_sign * digit^1)^-1 + local number = token('number', number_hexadecimal + + number_float + + number_decimal) + + -- callback for [=[ long strings ]=] + -- ps. LPeg is for Lua what regex is for Perl, which makes me smile :) + local equals = P '=' ^ 0 + local open = P '[' * Cg(equals, "init") * P '[' * P '\n' ^ -1 + local close = P ']' * C(equals) * P ']' + local closeeq = Cmt(close * Cb "init", function (s, i, a, b) return a == b end) + local longstring = open * C((P(1) - closeeq)^0) * close --/ 1 + + -- strings + local singlequoted_string = P "'" * ((1 - S "'\r\n\f\\") + (P '\\' * 1)) ^ 0 * "'" + local doublequoted_string = P '"' * ((1 - S '"\r\n\f\\') + (P '\\' * 1)) ^ 0 * '"' + local string = token('string', singlequoted_string + + doublequoted_string + + longstring) + + -- comments + local singleline_comment = P '--' * (1 - S '\r\n\f') ^ 0 + local multiline_comment = P '--' * longstring + local comment = token('comment', multiline_comment + singleline_comment) + + -- whitespace + local whitespace = token('space', S('\r\n\f\t ')^1) + + -- ordered choice of all tokens and last-resort error which consumes one character + local any_token = whitespace + number + keyword + ident + + string + comment + operator + token('error', 1) + + + table_of_tokens = Ct(any_token ^ 0) +end + +-- increment [line] by the number of line-ends in [text] +local function sync(line, text) + local index, limit = 1, #text + while index <= limit do + local start, stop = text:find('\r\n', index, true) + if not start then + start, stop = text:find('[\r\n\f]', index) + if not start then break end + end + index = stop + 1 + line = line + 1 + end + return line +end +lexer.sync = sync + +lexer.line = 0 + +-- we only need to synchronize the line-counter for these token types +local multiline_tokens = { comment = true, string = true, space = true } +lexer.multiline_tokens = multiline_tokens + +function lexer.scan_lua_tokenlist(input) + if not table_of_tokens then + lexer.init() + end + assert(type(input) == 'string', 'bad argument #1 (expected string)') + local line = 1 + local tokens = lpeg.match(table_of_tokens, input) + for i, token in pairs(tokens) do + local t = token[1] + if t == 'operator' or t == 'error' then + token[1] = token[2] + end + token[3] = line + if multiline_tokens[t] then + line = sync(line, token[2]) + end + end + return tokens +end + +--- get a token iterator from a source containing Lua code. +-- Note that this token iterator includes spaces and comments, and does not convert +-- string and number tokens - so e.g. a string token is quoted and a number token is +-- an unconverted string. +-- @param input the source - can be a string or a file-like object (i.e. read() returns line) +-- @param name for the source +function lexer.scan_lua(input,name) + if type(input) ~= 'string' and input.read then + input = input:read('*a') + end + local tokens = lexer.scan_lua_tokenlist(input) + local i, n = 1, #tokens + return function(k) + if k ~= nil then + k = i + k + if k < 1 or k > n then return nil end + return tokens[k] + end + local tok = tokens[i] + i = i + 1 + if tok then + lexer.line = tok[3] + lexer.name = name + return tok[1],tok[2] + end + end +end + +return lexer diff --git a/tools/LuaMacro/macro/lib/class.lua b/tools/LuaMacro/macro/lib/class.lua new file mode 100644 index 0000000..f762f36 --- /dev/null +++ b/tools/LuaMacro/macro/lib/class.lua @@ -0,0 +1,35 @@ +---- +-- a basic class mechanism. +-- Used for some of the demonstrations; the `class` macro in the `module` +-- package uses it. It provides a single function which returns a new 'class'. +-- The resulting object can be called to generate an instance of the class. +-- You may provide a base class for single inheritance; in this case, the functions +-- of the base class will be copied into the new class' metatable (so-called 'fat metatable') +-- +-- Example: +-- +-- local class = require 'macro.lib.class' +-- A = class() +-- function A._init(name) self.name = name end +-- a = A("hello") +-- assert(a.name == "hello") +-- +-- @module macro.lib.class + +return function (base) + -- OOP with single inheritance + local klass,cmt = {},{} + if base then -- 'fat metatable' inheritance + for k,v in pairs(base) do klass[k] = v end + end + klass.__index = klass + -- provide a callable constructor that invokes user-supplied ctor + function cmt:__call(...) + local obj = setmetatable({},klass) + if klass._init then klass._init(obj,...) + elseif base and base._init then base._init(base,...) end + return obj + end + setmetatable(klass,cmt) + return klass +end diff --git a/tools/LuaMacro/macro/lib/test.lua b/tools/LuaMacro/macro/lib/test.lua new file mode 100644 index 0000000..5fff39e --- /dev/null +++ b/tools/LuaMacro/macro/lib/test.lua @@ -0,0 +1,144 @@ +--- `assert_` macro library support. +-- This module may of course be used on its own; `assert_` merely provides +-- some syntactical sugar for its functionality. It is based on Penlight's +-- `pl.test` module. +-- @module macro.libs.test + +local test = {} + +local _eq,_tostring + +-- very much like tablex.deepcompare from Penlight +function _eq (v1,v2) + if type(v1) ~= type(v2) then return false end + -- if the value isn't a table, or it has defined the equality operator.. + local mt = getmetatable(v1) + if (mt and mt.__eq) or type(v1) ~= 'table' then + return v1 == v2 + end + -- both values are plain tables + if v1 == v2 then return true end -- they were the same table... + for k1,x1 in pairs(v1) do + local x2 = v2[k1] + if x2 == nil or not _eq(x1,x2) then return false end + end + for k2,x2 in pairs(v2) do + local x1 = v1[k2] + if x1 == nil or not _eq(x1,x2) then return false end + end + return true +end + +local function keyv (k) + if type(k) ~= 'string' then + k = '['..k..']' + end + return k +end + +function _tostring (val) + local mt = getmetatable(val) + if (mt and mt.__tostring) or type(val) ~= 'table' then + if type(val) == 'string' then + return '"'..tostring(val)..'"' + else + return tostring(val) + end + end + -- dump the table; doesn't need to be pretty! + local res = {} + local function put(s) res[#res+1] = s end + put '{' + for k,v in pairs(val) do + put(keyv(k)..'=') + put(_tostring(v)) + put ',' + end + table.remove(res) -- remove last ',' + put '}' + return table.concat(res) +end + +local function _lt (v1,v2) return v1 < v2 end +local function _gt (v1,v2) return v1 > v2 end +local function _match (v1,v2) return v1:match(v2) end + +local function _assert (v1,v2,cmp,msg) + if not cmp(v1,v2) then + print('first:',_tostring(v1)) + print(msg) + print('second:',_tostring(v2)) + error('assertion failed',3) + end +end + +--- assert if parameters are not equal. If the values are tables, +-- they will be compared by value. +-- @param v1 given value +-- @param v2 test value +function test.assert_eq (v1,v2) + _assert(v1,v2,_eq,"is not equal to"); +end + +--- assert if first parameter is not less than second. +-- @param v1 given value +-- @param v2 test value +function test.assert_lt (v1,v2) + _assert(v1,v2,_lt,"is not less than") +end + +--- assert if first parameter is not greater than second. +-- @param v1 given value +-- @param v2 test value +function test.assert_gt (v1,v2) + _assert(v1,v2,_gt,"is not greater than") +end + +--- assert if first parameter string does not match the second. +-- The condition is `v1:match(v2)`. +-- @param v1 given value +-- @param v2 test value +function test.assert_match (v1,v2) + _assert(v1,v2,_match,"does not match") +end + +-- return the error message from a function that raises an error. +-- Will raise an error if the function did not raise an error. +-- @param fun the function +-- @param ... any arguments to the function +-- @return the error message +function test.pcall_no(fun,...) + local ok,err = pcall(fun,...) + if ok then error('expression did not throw error',3) end + return err +end + +local tuple = {} + +function tuple.__eq (a,b) + if a.n ~= b.n then return false end + for i=1, a.n do + if not _eq(a[i],b[i]) then return false end + end + return true +end + +function tuple.__tostring (self) + local ts = {} + for i = 1,self.n do + ts[i] = _tostring(self[i]) + end + return '('..table.concat(ts,',')..')' +end + +--- create a tuple capturing multiple return values. +-- Equality between tuples means that all of their values are equal; +-- values may be `nil` +-- @param ... any values +-- @return a tuple object +function test.tuple(...) + return setmetatable({n=select('#',...),...},tuple) +end + +return test + diff --git a/tools/LuaMacro/macro/module.lua b/tools/LuaMacro/macro/module.lua new file mode 100644 index 0000000..a8e1b8d --- /dev/null +++ b/tools/LuaMacro/macro/module.lua @@ -0,0 +1,132 @@ +--[[--- +Easy no-fuss modules. + +Any function inside the module will be exported, unless it is explicitly +local. The functions are declared up front using patching, leading to efficient +calls between module functions. + + require_ 'module' + + function one () + return two() + end + + function two () + return 42 + end + +Classes can also be declared inside modules: + + require_ 'module' + + class A + function set(self,val) @val = val end + function get(self) return @val end + end + +Within class definitions, the macro `@` expands to either `self.` or `self:` depending +on context, and provides a Ruby-like shortcut. + +If you give these modules names with `m.lua` extension like `mod.m.lua`, then you can +simply use `require()` to use them with LuaMacro. + +@module macro.module +]] +local M = require 'macro' + +local locals, locals_with_value = {}, {} +local ref + +local function module_add_new_local (name) + locals[#locals+1] = name +end + +local function module_add_new_local_with_value (name,value) + locals_with_value[name] = value +end + + +local function was_local_function (get) + local tl,keyw = get:peek(-1) + return tl=='keyword' and keyw=='local' +end + +-- exclude explicitly local functions and anonymous functions. +M.keyword_handler('function',function(get) + local tn,name = get:peek(1) + local was_local = was_local_function(get) + if not was_local and tn == 'iden' then + module_add_new_local(name) + end +end) + +-- when the module is closed, this will patch the locals and +-- output the module table. +M.keyword_handler('END',function(get) + local concat = table.concat + local patch = '' + if next(locals_with_value) then + local lnames,lvalues = {},{} + local i = 1 + for k,v in pairs(locals_with_value) do + lnames[i] = k + lvalues[i] = v + i = i + 1 + end + patch = patch..'local '..concat(lnames,',')..'='..concat(lvalues,',')..'; ' + end + if #locals > 0 then + patch = patch .. 'local '..concat(locals,',') + end + get:patch(ref,patch) + local dcl = {} + for i,name in ipairs(locals) do + dcl[i] = name..'='..name + end + dcl = table.concat(dcl,', ') + return 'return {' .. dcl .. '}' +end) + +local no_class_require + +-- the meaning of @f is either 'self.f' for fields, or 'self:f' for methods. +local function at_handler (get,put) + local tn,name,tp = get:peek2(1) + M.assert(tn == 'iden','identifier must follow @') + return put:iden ('self',true) (tp=='(' and ':' or '.') +end + +local function method_handler (get,put) + local tn,name,tp = get:peek2() + if not was_local_function(get) and tn == 'iden' and tp == '(' then + return put ' ' :iden ('_C',true) '.' + end +end + +M.define ('_C_',function() + M.define_scoped('@',at_handler) + if not no_class_require then + module_add_new_local_with_value('_class','require "macro.lib.class"') + no_class_require = true + end + M.scoped_keyword_handler('function',method_handler) +end) + +M.define('class',function(get) + local base = '' + local name = get:iden() + if get:peek(1) == ':' then + get:next() + base = get:iden() + end + module_add_new_local(name) + return ('do local _C = _class(%s); %s = _C; _C_\n'):format(base,name) +end) + +-- the result of the macro is just a placeholder for the locals +return function(get,put) + ref = get:placeholder(put) + return put +end + + diff --git a/tools/LuaMacro/macro/try.lua b/tools/LuaMacro/macro/try.lua new file mode 100644 index 0000000..8e49eb0 --- /dev/null +++ b/tools/LuaMacro/macro/try.lua @@ -0,0 +1,47 @@ +--- A try/except block. +-- This generates syntactical sugar around `pcal`l, and correctly +-- distinguishes between the try block finishing naturally and +-- explicitly using 'return' with no value. This is handled by +-- converting any no value `return` to `return nil`. +-- +-- Apart from the usual creation of a closure, this uses a table +-- to capture all the results. Not likely to win speed contests, +-- but intended to be correct. +-- @module macro.try + +local M = require 'macro' + +local function pack (...) + local args = {...} + args.n = select('#',...) + return args +end + +function pcall_(fn,...) + return pack(pcall(fn,...)) +end + +local function check_return_value(get,put) + local t,v = get:next() + put:space() + if t=='keyword' and (v=='end' or v=='else' or v=='until') then + put:keyword 'nil' + end + return put(t,v) +end + + +M.define('RR_',M.make_scoped_handler('return',check_return_value)) + + +--- A try macro, paired with except. +-- +-- try +-- maybe_something_bad() +-- except (e) +-- print(e) +-- end +-- @macro try +M.define 'try do local r_ = pcall_(function() RR_ ' +M.define 'except(e) end); if r_[1] then if r_.n > 1 then return unpack(r_,2,r_.n) end else local e = r_[2] _END_END_ ' + diff --git a/tools/LuaMacro/macro/with.lua b/tools/LuaMacro/macro/with.lua new file mode 100644 index 0000000..de51590 --- /dev/null +++ b/tools/LuaMacro/macro/with.lua @@ -0,0 +1,31 @@ +--[[-- +A `with` statement. This works more like the Visual Basic statement than the +Pascal one; fields have an explicit period to indicate that they are special. +This makes variable scoping explcit. + + aLongTableName = {} + with aLongTableName do + .a = 1 + .b = {{x=1},{x=2}} + .c = {f = 2} + print(.a,.c.f,.b[1].x) + end + +Fields that follow an identifier or a `}` are passed as-is. + +@module macro.with +]] +local M = require 'macro' + +M.define('with',function(get,put) + M.define_scoped('.',function() + local lt,lv = get:peek(-1,true) -- peek before the period... + if lt ~= 'iden' and lt ~= ']' then + return '_var.' + else + return nil,true -- pass through + end + end) + local expr = get:upto 'do' + return 'do local _var = '..tostring(expr)..'; ' +end) diff --git a/tools/LuaMacro/readme.md b/tools/LuaMacro/readme.md new file mode 100644 index 0000000..e86bbfb --- /dev/null +++ b/tools/LuaMacro/readme.md @@ -0,0 +1,1010 @@ +## LuaMacro - a macro preprocessor for Lua + +This is a library and driver script for preprocessing and evaluating Lua code. +Lexical macros can be defined, which may be simple C-preprocessor style macros or +macros that change their expansion depending on the context. + +It is a new, rewritten version of the +[Luaforge](http://luaforge.net/projects/luamacro/) project of the same name, which +required the [token filter +patch](http://www.tecgraf.puc-rio.br/~lhf/ftp/lua/#tokenf) by Luiz Henrique de +Figueiredo. This patch allowed Lua scripts to filter the raw token stream before +the compiler stage. Within the limits imposed by the lexical filter approach this +worked pretty well. However, the token filter patch is unlikely to ever become +part of mainline Lua, either in its original or +[revised](http://lua-users.org/lists/lua-l/2010-02/msg00325.html) form. So the most +portable option becomes precompilation, but Lua bytecode is not designed to be +platform-independent and in any case changes faster than the surface syntax of the +language. So using LuaMacro with LuaJIT would have required re-applying the patch, +and would remain within the ghetto of specialized, experimental use. + +This implementation uses a [LPeg](http://www.inf.puc-rio.br/~roberto/lpeg.html) +lexical analyser originally by [Peter +Odding](http://lua-users.org/wiki/LpegRecipes) to tokenize Lua source, and builds +up a preprocessed string explicitly, which then can be loaded in the usual way. +This is not as efficient as the original, but it can be used by anyone with a Lua +interpreter, whether it is Lua 5.1, 5.2 or LuaJIT 2. An advantage of fully building +the output is that it becomes much easier to debug macros when you can actually see +the generated results. (Another example of a LPeg-based Lua macro preprocessor is +[Luma](http://luaforge.net/projects/luma/)) + +It is not possible to discuss macros in Lua without mentioning Fabien Fleutot's +[Metalua](metalua.luaforge.net/) which is an alternative Lua compiler which +supports syntactical macros that can work on the AST (Abstract Syntax Tree) itself +of Lua. This is clearly a technically superior way to extend Lua syntax, but again +has the disadvantage of being a direct-to-bytecode compiler. (Perhaps it's also a +matter of taste, since I find it easier to think about extending Lua on the lexical +level.) + +My renewed interest in Lua lexical macros came from some discussions on the Lua +mailing list about numerically optimal Lua code using LuaJIT. We have been spoiled +by modern optimizing C/C++ compilers, where hand-optimization is often discouraged, +but LuaJIT is new and requires some assistance. For instance, unrolling short loops +can make a dramatic difference, but Lua does not provide the key concept of +constant value to assist the compiler. So a very straightforward use of a macro +preprocessor is to provide named constants in the old-fashioned C way. Very +efficient code can be generated by generalizing the idea of 'varargs' into a +statically-compiled 'tuple' type. + + tuple(3) A,B + +The assigment `A = B` is expanded as: + + A_1,A_2,A_3 = B_1,B_2,B_3 + +I will show how the expansion can be made context-sensitive, so that the +loop-unrolling macro `do_` changes this behaviour: + + do_(i,1,3, + A = 0.5*B + ) + +expands to: + + A_1 = 0.5*B_1 + A_2 = 0.5*B_2 + A_3 = 0.5*B_3 + +Another use is crafting DSLs, particularly for end-user scripting. For instance, +people may be more comfortable with `forall x in t do` rather than `for _,x in +ipairs(t) do`; there is less to explain in the first form and it translates +directly to the second form. Another example comes from this common pattern: + + some_action(function() + ... + end) + +Using the following macro: + + def_ block (function() _END_CLOSE_ + +we can write: + + some_action block + ... + end + +A criticism of traditional lexical macros is that they don't respect the scoping +rules of the language itself. Bad experiences with the C preprocessor lead many to +regard them as part of the prehistory of computing. The macros described here can +be lexically scoped, and can be as 'hygenic' as necessary, since their expansion +can be finely controlled with Lua itself. + +For me, a more serious charge against 'macro magic' is that it can lead to a +private dialect of the language (the original Bourne shell was written in C +'skinned' to look like Algol 68.) This often indicates a programmer uncomfortable +with a language, who wants it to look like something more familiar. Relying on a +preprocessor may mean that programmers need to immerse themselves more in the idioms of +the new language. + +That being said, macros can extend a language so that it can be more expressive for +a particular task, particularly if the users are not professional programmers. + +### Basic Macro Substitution + +To install LuaMacro, expand the archive and make a script or batch file that points +to `luam.lua`, for instance: + + lua /home/frodo/luamacro/luam.lua $* + +(Or '%*' if on Windows.) Then put this file on your executable path. + +Any Lua code loaded with `luam` goes through four distinct steps: + + * loading and defining macros + * preprocessing + * compilation + * execution + +The last two steps happen within Lua itself, but always occur, even though the Lua +compiler is fast enough that we mostly do not bother to save the generated bytecode. + +For example, consider this `hello.lua`: + + print(HELLO) + +and `hello-def.lua`: + + local macro = require 'macro' + macro.define 'HELLO "Hello, World!"' + +To run the program: + + $> luam -lhello-def hello.lua + Hello, World! + +So the module `hello-def.lua` is first loaded (compiled and executed, but not +preprocessed) and only then `hello.lua` can be preprocessed and then loaded. + +Naturaly, there are easier ways to use LuaMacro, but I want to emphasize the +sequence of macro loading, preprocessing and script loading. `luam` has a `-d` +flag, meaning 'dump', which is very useful when debugging the output of the +preprocessing step: + + $> luam -d -lhello-def hello.lua + print("Hello, World!") + +`hello2.lua` is a more sensible first program: + + require_ 'hello-def' + print(HELLO) + +You cannot use the Lua `require` function at this point, since `require` is only +executed when the program starts executing and we want the macro definitions to be +available during the current compilation. `require_` is the macro version, which +loads the file at compile-time. + +New with 2.5 is the default @ shortcut available when using `luam`, +so `require_` can be written `@require`. +(`@` is itself a macro, so you can redefine it if needed.) + +There is also `include_/@include`, which is analogous to `#include` in `cpp`. It takes a +file path in quotes, and directly inserts the contents of the file into the current +compilation. Although tempting to use, it will not work here because again the +macro definitions will not be available at compile-time. + +`hello3.lua` fits much more into the C preprocessor paradigm, which uses the `def_` +macro: + + @def HELLO "Hello, World!" + print(HELLO) + +(Like `cpp`, such macro definitions end with the line; however, there is no +equivalent of `\` to extend the definition over multiple lines.) + +With 2.1, an alternative syntax `def_ (name body)` is also available, which can be +embedded inside a macro expression: + + def_ OF_ def_ (of elseif _value ==) + +Or even extend over several lines: + + def_ (complain(msg,n) + for i = 1,n do + print msg + end + ) + +`def_` works pretty much like `#define`, for instance, `def_ SQR(x) ((x)*(x))`. A +number of C-style favourites can be defined, like `assert_` using `_STR_`, which is +a predefined macro that 'stringifies' its argument. + + def_ assert_(condn) assert(condn,_STR_(condn)) + +`def_` macros are _lexically scoped_: + + local X = 1 + if something then + def_ X 42 + assert(X == 42) + end + assert(X == 1) + +LuaMacro keeps track of Lua block structure - in particular it knows when a +particular lexical scope has just been closed. This is how the `_END_CLOSE_` +built-in macro works + + def_ block (function() _END_CLOSE_ + + my_fun block + do_something_later() + end + +When the current scope closes with `end`, LuaMacro appends the necessary ')' to +make this syntax valid. + +A common use of macros in both C and Lua is to inline optimized code for a case. +The Lua function `assert()` always evaluates its second argument, which is not +always optimal: + + def_ ASSERT(condn,expr) if condn then else error(expr) end + + ASSERT(2 == 1,"damn! ".. 2 .." is not equal to ".. 1) + +If the message expression is expensive to execute, then this can give better +performance at the price of some extra code. `ASSERT` is now a statement, not a +function, however. + +### Conditional Compilation + +For this to work consistently, you need to use the `@` shortcut: + + @include 'test.inc' + @def A 10 + ... + +This makes macro 'preprocessor' statements stand out more. Conditional compilation +works as you would expect from C: + + -- test-cond.lua + @if A + print 'A defined' + @else + print 'A not defined' + @end + @if os.getenv 'P' + print 'Env P is defined' + @end + +Now, what is `A`? It is a Lua expression which is evaluated at _preprocessor_ +time, and if it returns any value except `nil` or `false` it is true, using +the usual Lua rule. Assuming `A` is just a global variable, how can it be set? + + $ luam test-cond.lua + A not defined + $ luam -VA test-cond.lua + A defined + $ export P=1 + $ luam test-cond.lua + A not defined + Env P is defined + +Although this looks very much like the standard C preprocessor, the implementation +is rather different - `@if` is a special macro which evaluates its argument +(everything on the rest of the line) as a _Lua expression_ +and skips upto `@end` (or `@else` or `@elseif`) if that condition is false. + + +### Using macro.define + +`macro.define` is less convenient than `def_` but much more powerful. The extended +form allows the substitution to be a _function_ which is called in-place at compile +time. These definitions must be loaded before they can be used, +either with `-l` or with `@require`. + + macro.define('DATE',function() + return '"'..os.date('%c')..'"' + end) + +Any text which is returned will be tokenized and inserted into the output stream. +The explicit quoting here is needed to ensure that `DATE` will be replaced by the +string "04/30/11 09:57:53". ('%c' gives you the current locale's version of the +date; for a proper version of this macro, best to use `os.date` [with more explicit +formats](http://www.lua.org/pil/22.1.html) .) + +This function can also return nothing, which allows you to write macro code purely +for its _side-effects_. + +Non-operator characters like `@`,`$`, etc can be used as macros. For example, say +you like shell-like notation `$HOME` for expanding environment variables in your +scripts. + + macro.define '$(x) os.getenv(_STR_(x))' + +A script can now say `$(PATH)` and get the expected expansion, Make-style. But we +can do better and support `$PATH` directly: + + macro.define('$',function(get) + local var = get:iden() + return 'os.getenv("'..var..'")' + end) + +If a macro has no parameters, then the substitution function receives a 'getter' +object. This provides methods for extracting various token types from the input +stream. Here the `$` macro must be immediately followed by an identifier. + +We can do better, and define `$` so that something like `$(pwd)` has the same +meaning as the Unix shell: + + macro.define('$',function(get) + local t,v = get() + if t == 'iden' then + return 'os.getenv("'..v..'")' + elseif t == '(' then + local rest = get:upto ')' + return 'os.execute("'..tostring(rest)..'")' + end + end) + +(The getter `get` is callable, and returns the type and value of the next token.) + +It is probably a silly example, but it illustrates how a macro can be overloaded +based on its lexical context. Much of the expressive power of LuaMacro comes from +allowing macros to fetch their own parameters in this way. It allows us to define +new syntax and go beyond 'pseudo-functions', which is more important for a +conventional-syntax language like Lua, rather than Lisp where everything looks like +a function anyway. These kinds of macros are called 'reader' macros in the Lisp world, +since they temporarily take over reading code. + +It is entirely possible for macros to create macros; that is what `def_` does. +Consider how to add the concept of `const` declarations to Lua: + + const N,M = 10,20 + +Here is one solution: + + macro.define ('const',function(get) + get() -- skip the space + local vars = get:idens '=' + local values = get:list '\n' + for i,name in ipairs(vars) do + macro.assert(values[i],'each constant must be assigned!') + macro.define_scoped(name,tostring(values[i])) + end + end) + +The key to making these constants well-behaved is `define_scoped`, which installs a +block handler which resets the macro to its original value, which is usually `nil`. +This test script shows how the scoping works: + + require_ 'const' + do + const N,M = 10,20 + do + const N = 5 + assert(N == 5) + end + assert(N == 10 and M == 20) + end + assert(N == nil and M == nil) + + +If we were designing a DSL intended for non-technical users, then we cannot just +say to them 'learn the language properly - go read PiL!'. It would be easier to +explain: + + forall x in {10,20,30} do + +than the equivalent generic `for` loop. `forall` can be implemented fairly simply +as a macro: + + macro.define('forall',function(get) + local var = get:iden() + local t,v = get:next() -- will be 'in' + local rest = tostring(get:upto 'do') + return ('for _,%s in ipairs(%s) do'):format(var,rest) + end) + +That is, first get the loop variable, skip `in`, grab everything up to `do` and +output the corresponding `for` statement. + +Useful macros can often be built using these new forms. For instance, here is a +simple list comprehension macro: + + macro.define('L(expr,select) '.. + '(function() local res = {} '.. + ' forall select do res[#res+1] = expr end '.. + 'return res end)()' + ) + +For example, `L(x^2,x in t)` will make a list of the squares of all elements in `t`. + +Why don't we use a long string here? Because we don't wish to insert any extra line +feeds in the output.`macro.forall` defines more sophisticated `forall` statements +and list comprehension expressions, but the principle is the same - see 'tests/test-forall.lua' + +There is a second argument passed to the substitution function, which is a 'putter' +object - an object for building token lists. For example, a useful shortcut for +anonymous functions: + + M.define ('\\',function(get,put) + local args = get:idens('(') + local body = get:list() + return put:keyword 'function' '(' : idens(args) ')' : + keyword 'return' : list(body) : space() : keyword 'end' + end) + +The `put` object has methods for appending particular kinds of tokens, such as +keywords and strings, and is also callable for operator tokens. These always return +the object itself, so the output can be built up with chaining. + +Consider `\x,y(x+y)`: the `idens` getter grabs a comma-separated list of identifier +names upto the given token; the `list` getter grabs a general argument list. It +returns a list of token lists and by default stops at ')'. This 'lambda' notation +was suggested by Luiz Henrique de Figueiredo as something easily parsed by any +token-filtering approach - an alternative notation `|x,y| x+y` has been +[suggested](http://lua-users.org/lists/lua-l/2009-12/msg00071.html) but is +generally impossible to implement using a lexical scanner, since it would have to +parse the function body as an expression. The `\\` macro also has the advantage +that the operator precedence is explicit: in the case of `\\(42,'answer')` it is +immediately clear that this is a function of no arguments which returns two values. + +I would not necessarily suggest that lambdas are a good thing in +production code, but they _can_ be useful in iteractive exploration and within tests. + +Macros with explicit parameters can define a substitution function, but this +function receives the values themselves, not the getter and putter objects. These +values are _token lists_ and must be converted into the expected types using the +token list methods: + + macro.define('test_(var,start,finish)',function(var,start,finish) + var,start,finish = var:get_iden(),start:get_number(),finish:get_number() + print(var,start,finish) + end) + + +Since no `put` object is received, such macros need to construct their own: + + local put = M.Putter() + ... + return put + +(They can of course still just return the substitution as text.) + +### Dynamically controlling macro expansion + +Consider this loop-unrolling macro: + + do_(i,1,3, + y = y + i + ) + +which will expand as + + y = y + 1 + y = y + 2 + y = y + 3 + +For each iteration, it needs to define a local macro `i` which expands to 1,2 and 3. + + macro.define('do_(v,s,f,stat)',function(var,start,finish,statements) + local put = macro.Putter() + var,start,finish = var:get_iden(),start:get_number(),finish:get_number() + macro.push_token_stack('do_',var) + for i = start, finish do + -- output `set_ ` + put:iden 'set_':iden(var):number(i):space() + put:tokens(statements) + end + -- output `undef_ ` + put:iden 'undef_':iden(var) + -- output `_POP_ 'do_'` + put:iden '_DROP_':string 'do_' + return put + end) + +Ignoring the macro stack manipulation for a moment, it works by inserting `set_` +macro assignments into the output. That is, the raw output looks like this: + + set_ i 1 + y = y + i + set_ i 2 + y = y + i + set_ i 2 + y = y + i + undef_ i + _DROP_ 'do_' + +It's important here to understand that LuaMacro does not do _recursive_ +substitution. Rather, the output of macros is pushed out to the stream which is +then further substituted, etc. So we do need these little helper macros to set the +loop variable at each point. + +Using the macro stack allows macros to be aware that they are expanding inside a +`do_` macro invocation. Consider `tuple`, which is another macro which creates +macros: + + tuple(3) A,B + A = B + +which would expand as + + local A_1,A_2,A_3,B_1,B_2,B_3 + A_1,A_2,A_3 = B_1,B_2,B_3 + +But we would like + + do_(i,1,3, + A = B/2 + ) + +to expand as + + A_1 = B_1/2 + A_2 = B_2/2 + A_2 = B_2/2 + +And here is the definition: + + macro.define('tuple',function(get) + get:expecting '(' + local N = get:number() + get:expecting ')' + get:expecting 'space' + local names = get:idens '\n' + for _,name in ipairs(names) do + macro.define(name,function(get,put) + local loop_var = macro.value_of_macro_stack 'do_' + if loop_var then + local loop_idx = tonumber(macro.get_macro_value(loop_var)) + return put:iden (name..'_'..loop_idx) + else + local out = {} + for i = 1,N do + out[i] = name..'_'..i + end + return put:idens(out) + end + end) + end + end) + +The first expansion case happens if we are not within a `do_` macro; a simple list +of names is outputted. Otherwise, we know what the loop variable is, and can +directly ask for its value. + +### Operator Macros + +You can of course define `@` to be a macro; a new feature allows you to add new +operator tokens: + + macro.define_tokens {'##','@-'} + +which can then be used with `macro.define`, but also now with `def_`. It's now +possible to define a list comprehension syntax that reads more naturally, e.g. +`{|x^2| i=1,10}` by making `{|` into a new token. + +Up to now, making a Lua operator token such as `.` into a macro was not so useful. +Such a macro may now return an extra value which indicates that the operator should +simply 'pass through' as is. Consider defining a `with` statement: + + with A do + .x = 1 + .y = 2 + end + +I've deliberately indicated the fields using a dot (a rare case of Visual Basic +syntax being superior to Delphi). So it is necessary to overload '.' and look at +the previous token: if it isn't a case like `name.` or `].` then we prepend the +table. Otherwise, the operator must simply _pass through_, to prevent an +uncontrolled recursion. + + M.define('with',function(get,put) + M.define_scoped('.',function() + local lt,lv = get:peek(-1,true) -- peek before the period... + if lt ~= 'iden' and lt ~= ']' then + return '_var.' + else + return nil,true -- pass through + end + end) + local expr = get:upto 'do' + return 'do local _var = '..tostring(expr)..'; ' + end) + +Again, scoping means that this behaviour is completely local to the with-block. + +A more elaborate experiment is `cskin.lua` in the tests directory. This translates +a curly-bracket form into standard Lua, and at its heart is defining '{' and '}' as +macros. You have to keep a brace stack, because these tokens still have their old +meaning and the table constructor in this example must still work, while the +trailing brace must be converted to `end`. + + if (a > b) { + t = {a,b} + } + +### Pass-Through Macros + +Normally a macro replaces the name (plus any arguments) with the substitution. It +is sometimes useful to pass the name through, but not to push the name into the +token stream - otherwise we will get an endless expansion. + + macro.define('fred',function() + print 'fred was found' + return nil, true + end) + +This has absolutely no effect on the preprocessed text ('fred' remains 'fred', but +has a side-effect. This happens if the substitution function returns a second +`true` value. You can look at the immediate lexical environment with `peek`: + + macro.define('fred',function(get) + local t,v = get:peek(1) + if t == 'string' then + local str = get:string() + return 'fred_'..str + end + return nil,true + end) + +Pass-through macros are useful when each macro corresponds to a Lua variable; they +allow such variables to have a dual role. + +An example would be Python-style lists. The [Penlight +List](http://stevedonovan.github.com/Penlight/api/modules/pl.List.html) class has +the same functionality as the built-in Python list, but does not have any +syntactical support: + + > List = require 'pl.List' + > ls = List{10,20,20} + > = ls:slice(1,2) + {10,20} + > ls:slice_assign(1,2,{10,11,20,21}) + > = ls + {10,11,20,21,30} + +It would be cool if we could add a little bit of custom syntax to make this more +natural. What we first need is a 'macro factory' which outputs the code to create +the lists, and also suitable macros with the same names. + + -- list [ = ] + M.define ('list',function(get) + get() -- skip space + -- 'list' acts as a 'type' followed by a variable list, which may be + -- followed by initial values + local values + local vars,endt = get:idens (function(t,v) + return t == '=' or (t == 'space' and v:find '\n') + end) + -- there is an initialization list + if endt[1] == '=' then + values,endt = get:list '\n' + else + values = {} + end + -- build up the initialization list + for i,name in ipairs(vars) do + M.define_scoped(name,list_check) + values[i] = 'List('..tostring(values[i] or '')..')' + end + local lcal = M._interactive and '' or 'local ' + return lcal..table.concat(vars,',')..' = '..table.concat(values,',')..tostring(endt) + end) + +Note that this is a fairly re-usable pattern; it requires the type constructor +(`List` in this case) and a type-specific macro function (`list_check`). The only +tricky bit is handling the two cases, so the `idens` method finds the end using a +function, not a simple token. `idens`, like `list`, returns the list and the token +that ended the list, so we can use `endt` to check. + + list a = {1,2,3} + list b + +becomes + + local a = List({1,2,3}) + local b = List() + +unless we are in interactive mode, where `local` is not appropriate! + +Each of these list macro/variables may be used in several ways: + + - directly `a` - no action! + - `a[i]` - plain table index + - `a[i:j]` - a list slice. Will be `a:slice(i,j)` normally, but must + be `a:slice_assign(i,j,RHS)` if on the right-hand side of an assignment. + +The substitution function checks these cases by appropriate look-ahead: + + function list_check (get,put) + local t,v = get:peek(1) + if t ~= '[' then return nil, true end -- pass-through; plain var reference + get:expecting '[' + local args = get:list(']',':') + -- it's just plain table access + if #args == 1 then return '['..tostring(args[1])..']',true end + + -- two items separated by a colon; use sensible defaults + M.assert(#args == 2, "slice has two arguments!") + local start,finish = tostring(args[1]),tostring(args[2]) + if start == '' then start = '1' end + if finish == '' then finish = '-1' end + + -- look ahead to see if we're on the left hand side of an assignment + if get:peek(1) == '=' then + get:next() -- skip '=' + local rest,eoln = get:upto '\n' + rest,eoln = tostring(rest),tostring(eoln) + return (':slice_assign(%s,%s,%s)%s'):format(start,finish,rest,eoln),true + else + return (':slice(%s,%s)'):format(start,finish),true + end + end + +This can be used interactively, like so (it requires the Penlight list library.) + + $> luam -llist -i + Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio + Lua Macro 2.3.0 Copyright (C) 2007-2011 Steve Donovan + > list a = {'one','two'} + > = a:map(\x(x:sub(1,1))) + {o,t} + > a:append 'three' + > a:append 'four' + > = a + {one,two,three,four} + > = a[2:3] + {two,three} + > = a[2:2] = {'zwei','twee'} + {one,zwei,twee,three,four} + > = a[1:2]..{'five'} + {one,zwei,five} + +### Preprocessing C + +With the 2.2 release, LuaMacro can preprocess C files, by the inclusion of a C LPeg +lexer based on work by Peter Odding. This may seem a semi-insane pursuit, given +that C already has a preprocessor, (which is widely considered a misfeature.) +However, the macros we are talking about are clever, they can maintain state, and +can be scoped lexically. + +One of the irritating things about C is the need to maintain separate include +files. It would be better if we could write a module like this: + + + // dll.c + #include "dll.h" + + export { + typedef struct { + int ival; + } MyStruct; + } + + export int one(MyStruct *ms) { + return ms->ival + 1 + } + + export int two(MyStruct *ms) { + return 2*ms->ival; + } + +and have the preprocessor generate an apppropriate header file: + + + #ifndef DLL_H + #define DLL_H + typedef struct { + int ival; + } MyStruct; + + int one(MyStruct *ms) ; + int two(MyStruct *ms) ; + #endif + +The macro `export` is straightforward: + + + M.define('export',function(get) + local t,v = get:next() + local decl,out + if v == '{' then + decl = tostring(get:upto '}') + decl = M.substitute_tostring(decl) + f:write(decl,'\n') + else + decl = v .. ' ' .. tostring(get:upto '{') + decl = M.substitute_tostring(decl) + f:write(decl,';\n') + out = decl .. '{' + end + return out + end) + +It looks ahead and if it finds a `{}` block it writes the block as text to a file +stream; otherwise writes out the function signature. `get:upto '}'` will do the +right thing here since it keeps track of brace level. To allow any other macro +expansions to take place, `substitute_tostring` is directly called. + +`tests/cexport.lua` shows how this idea can be extended, so that the generated +header is only updated when it changes. + +To preprocess C with `luam`, you need to specify the `-C` flag: + + luam -C -lcexport -o dll.c dll.lc + +Have a look at [lc](modules/macro.lc.html) which defines a simplified way to write +Lua bindings in C. Here is `tests/str.l.c`: + + // preprocess using luam -C -llc -o str.c str.l.c + #include + + module "str" { + + def at (Str s, Int i = 0) { + lua_pushlstring(L,&s[i-1],1); + return 1; + } + + def upto (Str s, Str delim = " ") { + lua_pushinteger(L, strcspn(s,delim) + 1); + return 1; + } + + } + +The result looks like this: + + // preprocess using luam -C -llc -o str.c str.l.c + #line 2 "str.lc" + #include + + #include + #include + #include + #ifdef WIN32 + #define EXPORT __declspec(dllexport) + #else + #define EXPORT + #endif + typedef const char *Str; + typedef const char *StrNil; + typedef int Int; + typedef double Number; + typedef int Boolean; + + + #line 6 "str.lc" + static int l_at(lua_State *L) { + const char *s = luaL_checklstring(L,1,NULL); + int i = luaL_optinteger(L,2,0); + + #line 7 "str.lc" + + lua_pushlstring(L,&s[i-1],1); + return 1; + } + + static int l_upto(lua_State *L) { + const char *s = luaL_checklstring(L,1,NULL); + const char *delim = luaL_optlstring(L,2," ",NULL); + + #line 12 "str.lc" + + lua_pushinteger(L, strcspn(s,delim) + 1); + return 1; + } + + static const luaL_reg str_funs[] = { + {"at",l_at}, + {"upto",l_upto}, + {NULL,NULL} + }; + + EXPORT int luaopen_str (lua_State *L) { + luaL_register (L,"str",str_funs); + + return 1; + } + +Note the line directives; this makes working with macro-ized C code much easier +when the inevitable compile and run-time errors occur. `lc` takes away some +of the more irritating bookkeeping needed in writing C extensions +(here I only have to mention function names once) + +`lc` was used for the [winapi](https://github.com/stevedonovan/winapi) project to +preprocess [this +file](https://github.com/stevedonovan/winapi/blob/master/winapi.l.c) +into [standard C](https://github.com/stevedonovan/winapi/blob/master/winapi.c). + +This used an extended version of `lc` which handled the largely superficial +differences between the Lua 5.1 and 5.2 API. + +(The curious thing is that `winapi` is my only project where I've leant on +LuaMacro, and it's all in C.) + +### A Simple Test Framework + +LuaMacro comes with yet another simple test framework - I apologize for this in +advance, because there are already quite enough. But consider it a demonstration +of how a little macro sugar can make tests more readable, even if you are +uncomfortable with them in production code (see `tests/test-test.lua`) + + require_ 'assert' + assert_ 1 == 1 + assert_ "hello" matches "^hell" + assert_ x.a throws 'attempt to index global' + +The last line is more interesting, since it's transparently wrapping +the offending expression in an anonymous function. The expanded output looks +like this: + + T_ = require 'macro.lib.test' + T_.assert_eq(1 ,1) + T_.assert_match("hello" ,"^hell") + T_.assert_match(T_.pcall_no(function() return x.a end),'attempt to index global') + +(This is a generally useful pattern - use macros to provide a thin layer of sugar +over the underlying library. The `macro.assert` module is only 75 lines long, with +comments - its job is to format code to make using the implementation easier.) + +Remember that the predefined meaning of @ is to convert `@name` into `name_`. So we +could just as easily say `@assert 1 == 1` and so forth. + +Lua functions often return multiple values or tables: + + two = \(40,2) + table2 = \({40,2}) + @assert two() == (40,2) + @assert table2() == {40,2} + +For a proper grown-up Lua testing framework +that uses LuaMacro, see [Specl](http://gvvaughan.github.io/specl). + + +### Implementation + +It is not usually necessary to understand the underlying representation of token +lists, but I present it here as a guide to understanding the code. + +#### Token Lists + +The token list representation of the expression `x+1` is: + + {{'iden','x'},{'+','+'},{'number','1'}} + +which is the form returned by the LPeg lexical analyser. Please note that there are +also 'space' and 'comment' tokens in the stream, which is a big difference from the +token-filter standard. + +The `TokenList` type defines `__tostring` and some helper methods for these lists. + +The following macro is an example of the lower-level coding needed without the +usual helpers: + + local macro = require 'macro' + macro.define('qw',function(get,put) + local append = table.insert + local t,v = get() + local res = {{'{','{'}} + t,v = get:next() + while t ~= ')' do + if t ~= ',' then + append(res,{'string','"'..v..'"'}) + append(res,{',',','}) + end + t,v = get:next() + end + append(res,{'}','}'}) + return res + end) + +We're using the getter `next` method to skip any whitespace, but building up the +substitution without a putter, just manipulating the raw token list. `qw` takes a +plain list of words, separated by spaces (and maybe commas) and makes it into a +list of strings. That is, + + qw(one two three) + +becomes + + {'one','two','three'} + +#### Program Structure + +The main loop of `macro.substitute` (towards end of `macro.lua`) summarizes the +operation of LuaMacro: + +There are two macro tables, `imacro` for classic name macros, and `smacro` for +operator style macros. They contain macro tables, which must have a `subst` field +containing the substitution and may have a `parms` field, which means that they +must be followed by their arguments in parentheses. + +A keywords table is chiefly used to track block scope, e.g. +`do`,`if`,`function`,etc means 'increase block level' and `end`,`until` means +'decrease block level'. At this point, any defined block handlers for this level +will be evaluated and removed. These may insert tokens into the stream, like +macros. This is how something like `_END_CLOSE_` is implemented: the `end` causes +the block level to decrease, which fires a block handler which passes `end` through +and inserts a closing `)`. + +Any keyword may also have an associated keyword handler, which works rather like a +macro substitution, except that the keyword itself is always passed through first. +(Allowing keywords as regular macros would generally be a bad idea because of the +recursive substitution problem.) + +The macro `subst` field may be a token list or a function. if it is a function then +that function is called, with the parameters as token lists if the macro defined +formal parameters, or with getter and setter objects if not. If the result is text +then it is parsed into a token list. diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 0000000..04f0c56 --- /dev/null +++ b/tools/README.md @@ -0,0 +1,4 @@ +存放 工具 目录 +1. lua-gdb +2. LuaMacro +3. behavior3editor \ No newline at end of file diff --git a/tools/behavior3editor/.gitignore b/tools/behavior3editor/.gitignore new file mode 100644 index 0000000..5d3f789 --- /dev/null +++ b/tools/behavior3editor/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +build/ +settings.json \ No newline at end of file diff --git a/tools/behavior3editor/.npmrc b/tools/behavior3editor/.npmrc new file mode 100644 index 0000000..efa0fc0 --- /dev/null +++ b/tools/behavior3editor/.npmrc @@ -0,0 +1 @@ +registry=https://registry.npm.taobao.org/ \ No newline at end of file diff --git a/tools/behavior3editor/.prettierrc b/tools/behavior3editor/.prettierrc new file mode 100644 index 0000000..4caff8a --- /dev/null +++ b/tools/behavior3editor/.prettierrc @@ -0,0 +1,7 @@ +{ + "endOfLine": "lf", + "semi": true, + "tabWidth": 4, + "printWidth": 100, + "trailingComma": "es5" +} diff --git a/tools/behavior3editor/README.md b/tools/behavior3editor/README.md new file mode 100644 index 0000000..dba7f94 --- /dev/null +++ b/tools/behavior3editor/README.md @@ -0,0 +1,64 @@ +# 行为树编辑器(桌面版) +这是一个直观、可视化的行为树编辑器,行为树的保存格式为json,可以让策划自行去实现AI,技能,buff等复杂的游戏逻辑,从而减少不必要的沟通成本和提升开发效率。 +![](readme/preview.png) + +## 使用方法 ++ 打开编辑器 ++ 工作区->节点定义->选择文件(json) ++ 工作区->选择目录(指定行为树所在的目录) ++ 工作区->另存为(将工作区保存起来,以便下次打开) ++ 行为树->新建 + +## 示例项目 ++ 工作区: sample/workspace.json ++ 节点定义: sample/node-config.json ++ 行为树目录: sample/workdir ++ 批处理脚本: sample/scripts + +Tips: workspace.json也可以手动编辑,加上isRelative可以让配置中的路径变成相对路径,这样就不需要团队中每个人在使用前都必须先创建工作区 + +## 节点定义 +```typescript +interface ArgsDefType { + name: string, // 字段名 + type: string, // 字段类型 + desc: string, // 字段中文描述 +} +interface BehaviorNodeTypeModel { + name: string; //节点名称 + type?: string; //节点分类(Composite,Decorator,Condition,Action) + desc?: string; //节点说明 + args?: ArgsDefType[]; //参数列表 + input?: string[]; //输入变量名 + output?: string[]; //输出变量名 + doc?: string; //文档说明(markdown格式) +} +``` +节点定义也是json格式,参照[sample/node-config.json](sample/node-config.json),编辑器不提供节点定义的编辑,强烈建议节点定义文件由代码生成 (参照示例项目[behavior3lua](https://github.com/zhandouxiaojiji/behavior3lua))。 + +## 编译与构建 +```shell +npm install # 安装依赖 +npm start # 运行测试 +npm run dist # 编译exe可执行文件 +``` + +## 技术栈 ++ react + ts ++ electron ++ antd ++ g6 + +## 示例行为树框架 ++ lua版本 [behavior3lua](https://github.com/zhandouxiaojiji/behavior3lua) ++ js/ts版本 计划中。 + +## TODO ++ 右键菜单 ++ 面板拖拽新建节点 ++ 变量自动补全 ++ 设置面板 ++ 禁止动画选项 + +## About +目前编辑器还处于非常简陋的阶段,有问题可以联系作者(QQ1013299930),本项目将长期维护,望前端大佬们多提点~ \ No newline at end of file diff --git a/tools/behavior3editor/common/BatchExec.ts b/tools/behavior3editor/common/BatchExec.ts new file mode 100644 index 0000000..7eedd92 --- /dev/null +++ b/tools/behavior3editor/common/BatchExec.ts @@ -0,0 +1,46 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { BehaviorNodeModel, BehaviorTreeModel } from './BehaviorTreeModel'; + +export default (scriptPath: string, dirPath: string) => { + console.log("run script", scriptPath); + const str = fs.readFileSync(scriptPath, "utf8"); + const script = eval(str); + const batchExec = (filePath: string) => { + const files = fs.readdirSync(filePath); + files.forEach((filename) => { + var filedir = path.join(filePath, filename); + const stats = fs.statSync(filedir); + if (stats.isFile() && filename.match(/.\.json$/)) { + const str = fs.readFileSync(filedir, "utf8"); + let tree: BehaviorTreeModel = JSON.parse(str); + if (script.processTree) { + tree = script.processTree(tree); + } + if (tree && script.processNode) { + const processNode = (node: BehaviorNodeModel) => { + script.processNode(node, tree); + if (node.children) { + for (let child of node.children) { + processNode(child); + } + } + } + processNode(tree.root); + } + if (tree) { + fs.writeFileSync( + filedir, + JSON.stringify(tree) + ); + } + + } + if (stats.isDirectory()) { + batchExec(filedir); + } + }); + } + batchExec(dirPath); + console.log("run script", scriptPath, "done!"); +} \ No newline at end of file diff --git a/tools/behavior3editor/common/BehaviorTreeModel.ts b/tools/behavior3editor/common/BehaviorTreeModel.ts new file mode 100644 index 0000000..56186cd --- /dev/null +++ b/tools/behavior3editor/common/BehaviorTreeModel.ts @@ -0,0 +1,53 @@ +import { TreeGraphData } from "@antv/g6/lib/types"; + +export interface ArgsOptional{ + name:string; + value:string|number; +} + +export interface ArgsDefType { + name: string; + type: string; + desc: string; + default: string; + options: ArgsOptional[]; +} +export interface BehaviorNodeTypeModel { + name: string; + type?: string; + desc?: string; + args?: ArgsDefType[]; + input?: string[]; + output?: string[]; + doc?: string; +} + +export interface BehaviorNodeModel { + id: number; + name: string; + desc?: string; + args?: { [key: string]: any }; + input?: string[]; + output?: string[]; + children?: BehaviorNodeModel[]; + debug?: boolean; +} + +export interface BehaviorTreeModel { + name: string; + desc?: string; + root: BehaviorNodeModel; +} + +export interface GraphNodeModel extends TreeGraphData { + name: string; + desc?: string; + args?: { [key: string]: any }; + input?: string[]; + output?: string[]; + children?: GraphNodeModel[]; + conf: BehaviorNodeTypeModel; + debug?: boolean; + + size?: number[]; +} diff --git a/tools/behavior3editor/common/MainEventType.ts b/tools/behavior3editor/common/MainEventType.ts new file mode 100644 index 0000000..99616fc --- /dev/null +++ b/tools/behavior3editor/common/MainEventType.ts @@ -0,0 +1,15 @@ +export default { + OPEN_FILE: "OPEN_FILE", + OPEN_DIR: "OPEN_DIR", + RELOAD_SERVER: "RELOAD_SERVER", + BATCH_EXEC: "BATCH_EXEC", + CREATE_TREE: "CREATE_TREE", + CREATE_NODE: "CREATE_NODE", + DELETE_NODE: "DELETE_NODE", + COPY_NODE: "COPY_NODE", + PASTE_NODE: "PASTE_NODE", + SAVE_ALL: "SAVE_ALL", + SAVE: "SAVE", + UNDO: "UNDO", + REDO: "REDO", +}; diff --git a/tools/behavior3editor/common/Utils.ts b/tools/behavior3editor/common/Utils.ts new file mode 100644 index 0000000..76d12e6 --- /dev/null +++ b/tools/behavior3editor/common/Utils.ts @@ -0,0 +1,176 @@ +import { TreeGraphData } from "@antv/g6/lib/types"; +import { BehaviorNodeModel, BehaviorTreeModel, GraphNodeModel } from "./BehaviorTreeModel"; +import Settings from "../main-process/Settings"; +import { remote } from "electron"; +import * as path from "path"; + +export const cloneNodeData = (nodeData: GraphNodeModel) => { + const newData: BehaviorNodeModel = { + id: Number(nodeData.id), + name: nodeData.name, + desc: nodeData.desc, + }; + if (nodeData.input) { + newData.input = []; + for (let v of nodeData.input) { + newData.input.push(v || ""); + } + } + if (nodeData.output) { + newData.output = []; + for (let v of nodeData.output) { + newData.output.push(v || ""); + } + } + if (nodeData.args) { + newData.args = {}; + for (let k in nodeData.args) { + let v = nodeData.args[k]; + newData.args[k] = v; + } + } + if (nodeData.children) { + newData.children = [] + for (let child of nodeData.children) { + newData.children.push(cloneNodeData(child)); + } + } + return newData; +}; + +export const refreshNodeId = (nodeData: GraphNodeModel, id?: number) => { + if (!id) { + id = 1; + } + nodeData.id = (id++).toString(); + if (nodeData.children) { + nodeData.children.forEach((child) => { + id = refreshNodeId(child, id); + }); + } + return id; +}; + +export const calcTreeNodeSize = (treeNode: GraphNodeModel) => { + var height = 40; + const updateHeight = (obj: any) => { + if (Array.isArray(obj) || (obj && Object.keys(obj).length > 0)) { + const { str, line } = toBreakWord(`参数:${JSON.stringify(obj)}`, 35); + height += 20 * line; + } + }; + updateHeight(treeNode.args); + updateHeight(treeNode.input); + updateHeight(treeNode.output); + return [200, height]; +}; + +export const createTreeData = (bNode: BehaviorNodeModel, settings: Settings) => { + const treeData: GraphNodeModel = { + id: bNode.id.toString(), + name: bNode.name, + desc: bNode.desc, + args: bNode.args, + input: bNode.input, + output: bNode.output, + debug: bNode.debug, + conf: settings.getNodeConf(bNode.name), + }; + treeData.size = calcTreeNodeSize(treeData); + if (bNode.children) { + treeData.children = []; + bNode.children.forEach((child) => { + treeData.children.push(createTreeData(child, settings)); + }); + } + calcTreeNodeSize(treeData); + return treeData; +}; + +export const createFileData = (gNode: GraphNodeModel) => { + const nodeData: BehaviorNodeModel = { + id: Number(gNode.id), + name: gNode.name, + desc: gNode.desc || undefined, + args: gNode.args || undefined, + input: gNode.input || undefined, + output: gNode.output || undefined, + debug: gNode.debug, + }; + if (gNode.children) { + nodeData.children = []; + gNode.children.forEach((child) => { + nodeData.children.push(createFileData(child)); + }); + } + return nodeData; +}; + +export const findParent = (node: TreeGraphData, id: string): TreeGraphData | null => { + if (node.children) { + for (let child of node.children) { + if (child.id == id) { + return node; + } else { + let parent = findParent(child, id); + if (parent) { + return parent; + } + } + } + } + return null; +}; + +export const findFromAllChildren = (node: TreeGraphData, id: string): TreeGraphData | null => { + if (node.id == id) { + return node; + } + if (node.children) { + for (let child of node.children) { + let found = findFromAllChildren(child, id); + if (found) { + return found; + } + } + } + return null; +}; + +export const getRemoteSettings = () => { + return remote.getGlobal("settings") as Settings; +}; + +export const fileName2treeName = (filename: string) => { + return path.basename(filename).slice(0, -5); +}; + +export const createNewTree = (filename: string) => { + const tree: BehaviorTreeModel = { + name: fileName2treeName(filename), + root: { + id: 1, + name: "Sequence", + desc: "新建行为树", + }, + }; + return tree; +}; + +export const toBreakWord = (str: string, len: number, char='\n') => { + var strTemp = ""; + var line = 1; + if(str.length <= len) { + return {str, line}; + } + while (str.length > len) { + strTemp += str.substr(0, len) + char; + str = str.substr(len, str.length); + line ++; + } + strTemp += str; + return { + str: strTemp, + line, + }; +} \ No newline at end of file diff --git a/tools/behavior3editor/index.html b/tools/behavior3editor/index.html new file mode 100644 index 0000000..ebb1968 --- /dev/null +++ b/tools/behavior3editor/index.html @@ -0,0 +1,14 @@ + + + + + + 行为树编辑器 + + + +
+ + + + \ No newline at end of file diff --git a/tools/behavior3editor/main-process/AppMenu.ts b/tools/behavior3editor/main-process/AppMenu.ts new file mode 100644 index 0000000..d999fcb --- /dev/null +++ b/tools/behavior3editor/main-process/AppMenu.ts @@ -0,0 +1,454 @@ +import { + Menu, + app, + dialog, + BrowserWindow, + MenuItem, + WebContents, + MenuItemConstructorOptions, +} from "electron"; +import * as fs from "fs"; +import * as Utils from "../common/Utils"; +import MainEventType from "../common/MainEventType"; +import { MainProcess } from "./MainProcess"; +import Settings from "./Settings"; + +const packageConf = require("../package.json"); + +export default class AppMenu { + private mainProcess: MainProcess; + private mainWindow: BrowserWindow; + private webContents: WebContents; + private settings: Settings; + + constructor(mainProcess: MainProcess) { + this.mainProcess = mainProcess; + this.mainWindow = mainProcess.mainWindow; + this.webContents = mainProcess.mainWindow.webContents; + this.settings = mainProcess.settings; + } + + createMenu() { + const menu: Menu = new Menu(); + menu.append(this.createFileMenu()); + menu.append(this.createEditMenu()); + menu.append(this.createNodeMenu()); + menu.append(this.createWorkspaceMenu()); + menu.append(this.createToolsMenu()); + return menu; + } + + private createFileMenu() { + const fileItems: MenuItemConstructorOptions[] = []; + for (let path of this.settings.recentFiles) { + fileItems.push({ + label: path, + click: () => { + console.log("open recent file", path); + }, + }); + } + + return new MenuItem({ + label: "行为树", + submenu: [ + { + label: "新建", + accelerator: "ctrl+n", + click: () => { + (async () => { + const res = await dialog.showSaveDialog({ + properties: ["showOverwriteConfirmation"], + filters: [{ name: "Json", extensions: ["json"] }], + }); + if (!res.canceled) { + const path = res.filePath; + fs.writeFileSync( + path, + JSON.stringify(Utils.createNewTree(path), null, 2) + ); + this.webContents.send(MainEventType.CREATE_TREE, path); + } + })(); + }, + }, + { + label: "打开文件", + accelerator: "Ctrl+O", + click: () => { + (async () => { + const res = await dialog.showOpenDialog({ + properties: ["openFile"], + filters: [{ name: "Json", extensions: ["json"] }], + }); + if (res.filePaths.length > 0) { + const path = res.filePaths[0]; + if (this.settings.recentFiles.indexOf(path) < 0) { + this.settings.recentFiles.unshift(path); + this.settings.save(); + this.mainProcess.rebuildMenu(); + } + this.webContents.send(MainEventType.OPEN_FILE, path); + } + })(); + }, + }, + { type: "separator" }, + { + label: "最近打开", + submenu: fileItems, + }, + + { type: "separator" }, + { + label: "保存", + accelerator: "ctrl+s", + click: () => { + this.webContents.send(MainEventType.SAVE); + }, + }, + { + label: "全部保存", + accelerator: "ctrl+shift+s", + click: () => { + this.webContents.send(MainEventType.SAVE_ALL); + }, + }, + { + label: "合并导出Json", + click: () => { + (async () => { + const res = await dialog.showSaveDialog({ + properties: ["showOverwriteConfirmation"], + filters: [{ name: "Json", extensions: ["json"] }], + }); + if (res.filePath) { + const curWorkspace = this.settings.curWorkspace; + curWorkspace.writeAllTrees(res.filePath, (err) => { + const msg = err ? err : "导出成功"; + dialog.showMessageBox({ + type: "info", + buttons: ["ok"], + message: msg, + }); + }); + } + })(); + }, + }, + ], + }); + } + + private createEditMenu() { + return new MenuItem({ + label: "编辑", + submenu: [ + { + label: "撤销", + accelerator: "ctrl+z", + click: () => { + this.webContents.send(MainEventType.UNDO); + } + }, + { + label: "恢复", + accelerator: "ctrl+y", + click: () => { + this.webContents.send(MainEventType.REDO); + } + }, + { type: "separator" }, + { + label: "新建节点", + accelerator: "insert", + click: () => { + this.webContents.send(MainEventType.CREATE_NODE, "unknow"); + }, + }, + { + label: "删除节点", + accelerator: "delete", + click: () => { + this.webContents.send(MainEventType.DELETE_NODE); + }, + }, + { type: "separator" }, + { + label: "复制节点", + accelerator: "ctrl+c", + role: "copy", + click: () => { + this.webContents.send(MainEventType.COPY_NODE); + }, + }, + { + label: "粘贴节点", + accelerator: "ctrl+v", + click: () => { + this.webContents.send(MainEventType.PASTE_NODE); + }, + }, + ] + }) + } + + private createWorkspaceMenu() { + const openWorkspace = (path: string) => { + const curWorkspace = this.settings.curWorkspace; + curWorkspace.setFilepath(path); + curWorkspace.load(); + this.settings.pushRecentWorkspace(path); + this.mainProcess.rebuildMenu(); + this.webContents.send( + MainEventType.OPEN_DIR, + curWorkspace.getWorkdir(), + curWorkspace.getFilepath() + ); + }; + const saveToNewPath = () => { + (async () => { + const res = await dialog.showSaveDialog({ + properties: ["showOverwriteConfirmation"], + filters: [{ name: "Json", extensions: ["json"] }], + }); + if (!res.canceled) { + this.settings.curWorkspace.setFilepath(res.filePath); + this.settings.curWorkspace.save(); + openWorkspace(res.filePath); + } + })(); + }; + const recentItems: MenuItemConstructorOptions[] = []; + for (let path of this.settings.recentWorkspaces) { + recentItems.push({ + label: path, + click: () => { + console.log("open recent workspace", path); + openWorkspace(path); + }, + }); + } + return new MenuItem({ + label: "工作区", + submenu: [ + { + label: "打开", + click: () => { + (async () => { + const res = await dialog.showOpenDialog({ + properties: ["openFile"], + filters: [{ name: "Json", extensions: ["json"] }], + }); + if (res.filePaths.length > 0) { + openWorkspace(res.filePaths[0]); + } + })(); + }, + }, + { + label: "保存", + click: () => { + if (this.settings.curWorkspace.getFilepath()) { + this.settings.curWorkspace.save(); + } else { + saveToNewPath(); + } + }, + }, + { + label: "另存为", + click: () => { + saveToNewPath(); + }, + }, + { + label: "刷新", + accelerator: "F5", + click: () => { + console.log("reload workspace"); + openWorkspace(this.settings.curWorkspace.getFilepath()); + }, + }, + { + label: "最近打开", + submenu: recentItems, + }, + { type: "separator" }, + { + label: "打开目录", + accelerator: "Ctrl+Shift+O", + click: () => { + if (!this.settings.curWorkspace.getNodeConfPath()) { + dialog.showErrorBox("警告", "请先指定节点定义配置!"); + return; + } + (async () => { + const res = await dialog.showOpenDialog({ + properties: ["openDirectory"], + }); + if (res.filePaths.length > 0) { + const curWorkspace = this.settings.curWorkspace; + const path = res.filePaths[0]; + curWorkspace.setWorkdir(path); + curWorkspace.save(); + this.webContents.send( + MainEventType.OPEN_DIR, + path, + curWorkspace.getFilepath() + ); + } + })(); + }, + }, + { + label: "节点定义", + submenu: [ + { + label: "选择文件", + click: () => { + (async () => { + const res = await dialog.showOpenDialog({ + properties: ["openFile"], + filters: [{ name: "Json", extensions: ["json"] }], + }); + if (res.filePaths.length > 0) { + const nodeConfigPath = res.filePaths[0]; + this.settings.curWorkspace.setNodeConfPath(nodeConfigPath); + this.mainProcess.rebuildMenu(); + } + })(); + }, + }, + { type: "separator" }, + { label: this.settings.nodeConfPath }, + ], + }, + { type: "separator" }, + { + label: "关闭", + click: () => { + app.quit(); + }, + }, + ], + }); + } + + private createToolsMenu() { + const serverItems: MenuItemConstructorOptions[] = []; + const curServerName = this.settings.serverName; + for (let model of this.settings.curWorkspace.getServers()) { + serverItems.push({ + label: `${model.name} ${model.host}`, + type: 'checkbox', + checked: curServerName == model.name, + click: () => { + this.settings.serverName = model.name; + this.mainProcess.rebuildMenu(); + } + }) + } + return new MenuItem({ + label: "开发工具", + submenu: [ + { + label: "热更", + accelerator: "Ctrl+R", + click: () => { + this.webContents.send(MainEventType.RELOAD_SERVER); + } + }, + { + label: "联调服务器", + submenu: serverItems, + }, + { + label: "批处理脚本", + click: () => { + (async () => { + const res = await dialog.showOpenDialog({ + properties: ["openFile"], + filters: [{ name: "Javascript", extensions: ["js"] }], + }); + if (res.filePaths.length > 0) { + this.webContents.send(MainEventType.BATCH_EXEC, res.filePaths[0]); + } + })(); + } + }, + { + type: 'separator', + }, + { + label: "打开控制台", + accelerator: "Ctrl+Shift+I", + click: (_, browserWindow) => { + browserWindow.webContents.toggleDevTools(); + }, + }, + { + label: "重载编辑器", + click: (_, browserWindow) => { + browserWindow.reload(); + }, + }, + { + type: 'separator', + }, + { + label: `当前版本:${packageConf.version}` + } + ], + }); + } + + private createNodeMenu() { + const classifyItems: MenuItemConstructorOptions[] = []; + const map: { [key: string]: MenuItemConstructorOptions } = {}; + for (let t of this.settings.nodeClassify) { + let item: MenuItemConstructorOptions = { + id: t.classify, + label: `${t.classify}(${t.desc})`, + submenu: [], + }; + classifyItems.push(item); + map[t.classify] = item; + } + const other: MenuItemConstructorOptions = { + id: "other", + label: "其它", + submenu: [], + }; + var hasOther = false; + + for (let node of this.settings.nodeConfig) { + const item: MenuItemConstructorOptions = { + label: `${node.name}(${node.desc})`, + click: () => { + console.log("create node", node.name); + this.webContents.send(MainEventType.CREATE_NODE, node.name); + }, + }; + let typeItem = map[node.type]; + if (!typeItem) { + typeItem = other; + hasOther = true; + } + if (typeItem.submenu instanceof Menu) { + console.log("typeItem.submenu error", typeItem); + } else { + typeItem.submenu.push(item); + } + } + + if (hasOther) { + classifyItems.push(other); + } + + return new MenuItem({ + label: "新建节点", + submenu: classifyItems, + }); + } +} diff --git a/tools/behavior3editor/main-process/MainProcess.tsx b/tools/behavior3editor/main-process/MainProcess.tsx new file mode 100644 index 0000000..12a1a0b --- /dev/null +++ b/tools/behavior3editor/main-process/MainProcess.tsx @@ -0,0 +1,70 @@ +import { app, BrowserWindow, Menu, MenuItem, dialog, nativeTheme } from "electron"; +import AppMenu from "./AppMenu"; +import Settings from "./Settings"; +import MainEventType from "../common/MainEventType"; + +import electronLocalshortcut from "electron-localshortcut"; + +// 一些暴露给render-process的全局变量 +export interface Global { + settings: Settings; +} +declare var global: Global; + +export class MainProcess { + mainWindow: BrowserWindow; + appMenu: AppMenu; + settings: Settings; + constructor() { + nativeTheme.themeSource = "dark"; + app.on("ready", () => { + this.createWindow(); + electronLocalshortcut.register(this.mainWindow, "Ctrl+C", () => { + this.mainWindow.webContents.send(MainEventType.COPY_NODE); + }); + electronLocalshortcut.register(this.mainWindow, "Ctrl+V", () => { + this.mainWindow.webContents.send(MainEventType.PASTE_NODE); + }); + }); + app.on("window-all-closed", () => { + if (process.platform !== "darwin") { + app.quit(); + } + }); + app.on("activate", () => { + if (this.mainWindow === null) { + this.createWindow(); + } + }); + } + + createWindow() { + this.settings = new Settings(); + global.settings = this.settings; + + this.mainWindow = new BrowserWindow({ + width: 1280, + height: 800, + webPreferences: { + nodeIntegration: true, + enableRemoteModule: true, + }, + // fullscreenable:false, + // maximizable:false + }); + this.mainWindow.maximize(); + // mainWindow.webContents.openDevTools(); + this.mainWindow.loadFile("index.html"); + this.mainWindow.on("closed", function () { + this.mainWindow = null; + }); + this.appMenu = new AppMenu(this); + this.rebuildMenu(); + } + + rebuildMenu() { + Menu.setApplicationMenu(this.appMenu.createMenu()); + } +} + +export default new MainProcess(); diff --git a/tools/behavior3editor/main-process/Settings.ts b/tools/behavior3editor/main-process/Settings.ts new file mode 100644 index 0000000..4a8b94a --- /dev/null +++ b/tools/behavior3editor/main-process/Settings.ts @@ -0,0 +1,154 @@ +import * as fs from "fs"; +import Workspace from "./Workspace"; + +export interface BehaviorNodeClassify { + classify: string; + desc?: string; +} + +export interface SettingsModel { + recentWorkspaces?: string[]; + recentFiles?: string[]; + serverName?: string; + nodeClassify?: BehaviorNodeClassify[]; + treesDesc?: { [name: string]: string }; +} + +const settingPath = "settings.json"; +const sampleNodeClassify: BehaviorNodeClassify[] = [ + { classify: "Composite", desc: "复合节点" }, + { classify: "Decorator", desc: "修饰节点" }, + { classify: "Condition", desc: "条件节点" }, + { classify: "Action", desc: "行为节点" }, +]; + +export default class Settings { + private model: SettingsModel; + private dirty: boolean = false; + curWorkspace?: Workspace; + + constructor() { + this.load(); + + setInterval(() => { + if (this.dirty) { + fs.writeFileSync(settingPath, JSON.stringify(this.model, null, 2)); + this.dirty = false; + } + }, 1000) + } + + get nodeConfPath() { + return this.curWorkspace.getNodeConfPath(); + } + get recentWorkspaces() { + return this.model.recentWorkspaces; + } + get recentFiles() { + return this.model.recentFiles; + } + get nodeConfig() { + return this.curWorkspace.nodeConf; + } + get nodeClassify() { + return this.model.nodeClassify; + } + get serverModel() { + const servers = this.curWorkspace.getServers() + if (!servers) { + return; + } + for (let server of servers) { + if (server.name == this.model.serverName) { + return server; + } + } + return servers[0]; + } + get serverName() { + const servers = this.curWorkspace.getServers() + if (!servers) { + return ""; + } + for (let server of servers) { + if (server.name == this.model.serverName) { + return this.model.serverName; + } + } + if (servers[0]) { + return servers[0].name; + } else { + return '' + } + } + set serverName(name: string) { + this.model.serverName = name; + } + + set(config: SettingsModel) { + this.model = { + ...this.model, + ...config, + }; + this.save(); + } + + load() { + if (fs.existsSync(settingPath)) { + const str = fs.readFileSync(settingPath, "utf8"); + this.model = JSON.parse(str); + if (!this.model.treesDesc) { + this.model.treesDesc = {}; + this.save(); + } + } else { + this.model = { + recentWorkspaces: ["sample/workspace.json"], + recentFiles: [], + nodeClassify: sampleNodeClassify, + }; + this.save(); + } + this.curWorkspace = new Workspace(this.model.recentWorkspaces[0]); + this.curWorkspace.load(); + } + + save() { + this.dirty = true; + } + + getNodeConf(name: string) { + return this.curWorkspace.getNodeConf(name); + } + + pushRecentWorkspace(path: string) { + var list = this.model.recentWorkspaces; + if (list.indexOf(path) >= 0) { + list = list.filter((value) => value !== path); + } + list.unshift(path); + while (list.length > 10) { + list.pop(); + } + this.model.recentWorkspaces = list; + this.save(); + } + + getTreeDesc(name: string) { + const key = this.curWorkspace.getFilepath() + ' ' + name; + var desc = this.model.treesDesc[key]; + if (!desc) { + const str = fs.readFileSync(name, "utf8"); + const tree = JSON.parse(str); + desc = tree.desc ? tree.desc : ''; + this.model.treesDesc[key] = desc; + this.save(); + } + return desc; + } + + setTreeDesc(name: string, desc?: string) { + const key = this.curWorkspace.getFilepath() + ' ' + name; + this.model.treesDesc[key] = desc; + } +} diff --git a/tools/behavior3editor/main-process/Workspace.ts b/tools/behavior3editor/main-process/Workspace.ts new file mode 100644 index 0000000..d1d4eff --- /dev/null +++ b/tools/behavior3editor/main-process/Workspace.ts @@ -0,0 +1,157 @@ +import * as fs from "fs"; +import { BehaviorNodeTypeModel } from "../common/BehaviorTreeModel"; +import * as glob from "glob"; +import * as path from "path"; + +export interface ServerModel { + name: string; + host: string; +} + +export interface WorkspaceModel { + isRelative?: boolean; + nodeConfPath: string; + workdir: string; + servers?: ServerModel[]; +} + +const unknowNodeType: BehaviorNodeTypeModel = { + name: "unknow", + desc: "新建节点", + type: "Action", +}; + +export default class Workspace { + private filepath: string; + private nodeConfPath: string; + private workdir: string; + private model: WorkspaceModel; + + private name2conf: { [name: string]: BehaviorNodeTypeModel } = {}; + private types: BehaviorNodeTypeModel[] = []; + + constructor(filepath: string) { + this.filepath = filepath; + } + + load() { + if (!this.filepath) { + return; + } + try { + const str = fs.readFileSync(this.filepath, "utf8"); + const model = JSON.parse(str) as WorkspaceModel; + if (model.isRelative) { + const root = path.dirname(this.filepath); + this.nodeConfPath = path.join(root,model.nodeConfPath); + this.workdir = path.join(root,model.workdir); + } else { + this.nodeConfPath = model.nodeConfPath; + this.workdir = model.workdir; + } + this.model = model; + + this.initNodeConf(); + } catch (error) { + console.log(error); + } + } + + private initNodeConf() { + if (!this.nodeConfPath) { + return; + } + + const types: BehaviorNodeTypeModel[] = JSON.parse( + fs.readFileSync(this.nodeConfPath, "utf8") + ); + this.name2conf = {}; + types.forEach((t) => { + this.name2conf[t.name] = t; + }); + this.types = types; + } + + getNodeConfPath() { + return this.nodeConfPath; + } + setNodeConfPath(path: string) { + this.nodeConfPath = path; + this.initNodeConf(); + } + getWorkdir(): string { + return this.workdir; + } + setWorkdir(workdir: string) { + this.workdir = workdir; + } + getServers() { + if(this.model && this.model.servers) { + return this.model.servers; + } else { + return []; + } + } + getModel() { + return this.model; + } + + writeAllTrees(outFilePath: string, cb?: (err: string) => void) { + if (!fs.existsSync(this.getWorkdir())) { + cb && cb("请先打开工作区"); + return; + } + glob.glob(path.join(this.getWorkdir(), "**/*.json"), function (err, files) { + if (err) { + console.error(err); + cb && cb(err.message); + return; + } + + let result = new Array(); + files.forEach((file) => { + const data = fs.readFileSync(file, "utf8"); + result.push(JSON.parse(data)); + }); + + fs.writeFileSync(outFilePath, JSON.stringify(result, null, 2)); + cb && cb(null); + }); + } + + save(filepath?: string) { + if (filepath) { + this.filepath = filepath; + } + if (!this.filepath) { + return; + } + fs.writeFileSync( + this.filepath, + JSON.stringify( + { + nodeConfPath: this.nodeConfPath, + workdir: this.workdir, + }, + null, + 2 + ) + ); + } + + get nodeConf() { + return this.types; + } + + getFilepath() { + return this.filepath; + } + + setFilepath(filepath: string) { + this.filepath = filepath; + } + + getNodeConf(name: string) { + return this.name2conf[name] || unknowNodeType; + } +} diff --git a/tools/behavior3editor/package-lock.json b/tools/behavior3editor/package-lock.json new file mode 100644 index 0000000..45ba7f7 --- /dev/null +++ b/tools/behavior3editor/package-lock.json @@ -0,0 +1,9057 @@ +{ + "name": "behavior3editor", + "version": "0.1.4", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "7zip-bin": { + "version": "5.0.3", + "resolved": "https://registry.npm.taobao.org/7zip-bin/download/7zip-bin-5.0.3.tgz", + "integrity": "sha1-vFtVMuyv2SOmHy+wl+OxCMAQaj8=", + "dev": true + }, + "@ant-design/colors": { + "version": "4.0.5", + "resolved": "https://registry.npm.taobao.org/@ant-design/colors/download/@ant-design/colors-4.0.5.tgz?cache=0&sync_timestamp=1596611425139&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40ant-design%2Fcolors%2Fdownload%2F%40ant-design%2Fcolors-4.0.5.tgz", + "integrity": "sha1-19EA11Rcyo9iSVRgSmiS/Ei6Wq4=", + "requires": { + "tinycolor2": "^1.4.1" + } + }, + "@ant-design/css-animation": { + "version": "1.7.3", + "resolved": "https://registry.npm.taobao.org/@ant-design/css-animation/download/@ant-design/css-animation-1.7.3.tgz?cache=0&sync_timestamp=1596104751065&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40ant-design%2Fcss-animation%2Fdownload%2F%40ant-design%2Fcss-animation-1.7.3.tgz", + "integrity": "sha1-YKHJcAFOhrKPlAUQ1p5QPkKPETY=" + }, + "@ant-design/icons": { + "version": "4.3.0", + "resolved": "https://registry.npm.taobao.org/@ant-design/icons/download/@ant-design/icons-4.3.0.tgz", + "integrity": "sha1-Qg4M1SdIbA/lf4ExDWgZUPxM+s8=", + "requires": { + "@ant-design/colors": "^5.0.0", + "@ant-design/icons-svg": "^4.0.0", + "@babel/runtime": "^7.11.2", + "classnames": "^2.2.6", + "insert-css": "^2.0.0", + "rc-util": "^5.0.1" + }, + "dependencies": { + "@ant-design/colors": { + "version": "5.0.0", + "resolved": "https://registry.npm.taobao.org/@ant-design/colors/download/@ant-design/colors-5.0.0.tgz", + "integrity": "sha1-Rrc7TMaTWzX8i4RVXo5CyM/BkOY=", + "requires": { + "@ctrl/tinycolor": "^3.1.6" + } + } + } + }, + "@ant-design/icons-svg": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/@ant-design/icons-svg/download/@ant-design/icons-svg-4.1.0.tgz", + "integrity": "sha1-SAsCX0sg73/o9H1KSEbk/uhOoGw=" + }, + "@ant-design/react-slick": { + "version": "0.27.11", + "resolved": "https://registry.npm.taobao.org/@ant-design/react-slick/download/@ant-design/react-slick-0.27.11.tgz?cache=0&sync_timestamp=1602318517470&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40ant-design%2Freact-slick%2Fdownload%2F%40ant-design%2Freact-slick-0.27.11.tgz", + "integrity": "sha1-zniDEu2OZPy6L3u0VW9HSGtAfG4=", + "requires": { + "@babel/runtime": "^7.10.4", + "classnames": "^2.2.5", + "json2mq": "^0.2.0", + "lodash": "^4.17.15", + "resize-observer-polyfill": "^1.5.0" + } + }, + "@antv/color-util": { + "version": "2.0.5", + "resolved": "https://registry.npm.taobao.org/@antv/color-util/download/@antv/color-util-2.0.5.tgz", + "integrity": "sha1-bGwUrShzUmvLpyOAt4GiskNgrG4=", + "requires": { + "@antv/util": "^2.0.9", + "tslib": "^1.10.0" + } + }, + "@antv/dom-util": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/@antv/dom-util/download/@antv/dom-util-2.0.2.tgz", + "integrity": "sha1-THsKGV4CflVXBDWmglzsVt+wtZA=", + "requires": { + "tslib": "^1.10.0" + } + }, + "@antv/event-emitter": { + "version": "0.1.2", + "resolved": "https://registry.npm.taobao.org/@antv/event-emitter/download/@antv/event-emitter-0.1.2.tgz", + "integrity": "sha1-oXt8uG5tBxiA3Gv7IydW+IYk7Lw=" + }, + "@antv/g-base": { + "version": "0.5.1", + "resolved": "https://registry.npm.taobao.org/@antv/g-base/download/@antv/g-base-0.5.1.tgz?cache=0&sync_timestamp=1600174460559&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40antv%2Fg-base%2Fdownload%2F%40antv%2Fg-base-0.5.1.tgz", + "integrity": "sha1-RmuHGa6Z5NJuTh//renbF2AA3h4=", + "requires": { + "@antv/event-emitter": "^0.1.1", + "@antv/g-math": "^0.1.5", + "@antv/matrix-util": "^3.1.0-beta.1", + "@antv/path-util": "~2.0.5", + "@antv/util": "~2.0.0", + "@types/d3-timer": "^1.0.9", + "d3-ease": "^1.0.5", + "d3-interpolate": "^1.3.2", + "d3-timer": "^1.0.9", + "detect-browser": "^5.1.0" + }, + "dependencies": { + "@antv/matrix-util": { + "version": "3.1.0-beta.2", + "resolved": "https://registry.npm.taobao.org/@antv/matrix-util/download/@antv/matrix-util-3.1.0-beta.2.tgz", + "integrity": "sha1-tK+vtw299Sr/yjCNNUbIoJD9I8o=", + "requires": { + "@antv/util": "^2.0.9", + "gl-matrix": "^3.3.0", + "tslib": "^1.10.0" + } + } + } + }, + "@antv/g-canvas": { + "version": "0.5.1", + "resolved": "https://registry.npm.taobao.org/@antv/g-canvas/download/@antv/g-canvas-0.5.1.tgz?cache=0&sync_timestamp=1600174461256&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40antv%2Fg-canvas%2Fdownload%2F%40antv%2Fg-canvas-0.5.1.tgz", + "integrity": "sha1-N+9CK35jku930/HnN/LWmXagDEE=", + "requires": { + "@antv/g-base": "^0.5.1", + "@antv/g-math": "^0.1.5", + "@antv/matrix-util": "^3.1.0-beta.1", + "@antv/path-util": "~2.0.5", + "@antv/util": "~2.0.0", + "gl-matrix": "^3.0.0" + }, + "dependencies": { + "@antv/matrix-util": { + "version": "3.1.0-beta.2", + "resolved": "https://registry.npm.taobao.org/@antv/matrix-util/download/@antv/matrix-util-3.1.0-beta.2.tgz", + "integrity": "sha1-tK+vtw299Sr/yjCNNUbIoJD9I8o=", + "requires": { + "@antv/util": "^2.0.9", + "gl-matrix": "^3.3.0", + "tslib": "^1.10.0" + } + } + } + }, + "@antv/g-math": { + "version": "0.1.5", + "resolved": "https://registry.npm.taobao.org/@antv/g-math/download/@antv/g-math-0.1.5.tgz?cache=0&sync_timestamp=1600174459928&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40antv%2Fg-math%2Fdownload%2F%40antv%2Fg-math-0.1.5.tgz", + "integrity": "sha1-jkSYp7wCsRPBG1A3OpsVJzE5ze8=", + "requires": { + "@antv/util": "~2.0.0", + "gl-matrix": "^3.0.0" + } + }, + "@antv/g-svg": { + "version": "0.5.1", + "resolved": "https://registry.npm.taobao.org/@antv/g-svg/download/@antv/g-svg-0.5.1.tgz?cache=0&sync_timestamp=1600174462035&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40antv%2Fg-svg%2Fdownload%2F%40antv%2Fg-svg-0.5.1.tgz", + "integrity": "sha1-JzRXYrgVItAwYLt2YtmAIcMWD8k=", + "requires": { + "@antv/g-base": "^0.5.1", + "@antv/g-math": "^0.1.5", + "@antv/util": "~2.0.0", + "detect-browser": "^5.0.0" + } + }, + "@antv/g6": { + "version": "3.8.1", + "resolved": "https://registry.npm.taobao.org/@antv/g6/download/@antv/g6-3.8.1.tgz", + "integrity": "sha1-HfdxSjCBsdGXAxrZxFBePM+Yxfc=", + "requires": { + "@antv/color-util": "^2.0.5", + "@antv/dom-util": "^2.0.1", + "@antv/event-emitter": "~0.1.0", + "@antv/g-base": "^0.5.1", + "@antv/g-canvas": "^0.5.1", + "@antv/g-math": "^0.1.1", + "@antv/g-svg": "^0.5.1", + "@antv/hierarchy": "^0.6.2", + "@antv/matrix-util": "^2.0.4", + "@antv/path-util": "^2.0.3", + "@antv/scale": "^0.3.1", + "@antv/util": "~2.0.5", + "d3-force": "^2.0.1", + "dagre": "^0.8.5", + "insert-css": "^2.0.0", + "ml-matrix": "^6.5.0" + } + }, + "@antv/gl-matrix": { + "version": "2.7.1", + "resolved": "https://registry.npm.taobao.org/@antv/gl-matrix/download/@antv/gl-matrix-2.7.1.tgz", + "integrity": "sha1-rLjjf3qz3wE0WrpDcteUK+QuuhQ=" + }, + "@antv/hierarchy": { + "version": "0.6.6", + "resolved": "https://registry.npm.taobao.org/@antv/hierarchy/download/@antv/hierarchy-0.6.6.tgz", + "integrity": "sha1-WEWbCO8ZuzTY3wqW1SlUWAeDrr4=", + "requires": { + "@antv/util": "^2.0.7" + } + }, + "@antv/matrix-util": { + "version": "2.0.7", + "resolved": "https://registry.npm.taobao.org/@antv/matrix-util/download/@antv/matrix-util-2.0.7.tgz", + "integrity": "sha1-pRkFmdQkwVGKG16zpUgnHX6Y/EQ=", + "requires": { + "@antv/gl-matrix": "^2.7.1", + "@antv/util": "^2.0.7", + "tslib": "^1.10.0" + } + }, + "@antv/path-util": { + "version": "2.0.8", + "resolved": "https://registry.npm.taobao.org/@antv/path-util/download/@antv/path-util-2.0.8.tgz", + "integrity": "sha1-m8VNK3GYqtmIqcxKbekFTyaRm4Q=", + "requires": { + "@antv/util": "^2.0.9", + "tslib": "^1.10.0" + } + }, + "@antv/scale": { + "version": "0.3.4", + "resolved": "https://registry.npm.taobao.org/@antv/scale/download/@antv/scale-0.3.4.tgz", + "integrity": "sha1-/t/a7f0+RaD0O/eG6ElnN12J364=", + "requires": { + "@antv/util": "~2.0.3", + "fecha": "~4.2.0", + "tslib": "^2.0.0" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/tslib/download/tslib-2.0.3.tgz", + "integrity": "sha1-jgdBrEX8DCJuWKF7/D5kubxsphw=" + } + } + }, + "@antv/util": { + "version": "2.0.9", + "resolved": "https://registry.npm.taobao.org/@antv/util/download/@antv/util-2.0.9.tgz", + "integrity": "sha1-vT4pajkuEfvieB/eOdjnC6HFPtA=", + "requires": { + "tslib": "^1.10.0" + } + }, + "@babel/runtime": { + "version": "7.11.2", + "resolved": "https://registry.npm.taobao.org/@babel/runtime/download/@babel/runtime-7.11.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fruntime%2Fdownload%2F%40babel%2Fruntime-7.11.2.tgz", + "integrity": "sha1-9UnBPHVMxAuHZEufqfCaapX+BzY=", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@ctrl/tinycolor": { + "version": "3.1.7", + "resolved": "https://registry.npm.taobao.org/@ctrl/tinycolor/download/@ctrl/tinycolor-3.1.7.tgz?cache=0&sync_timestamp=1605289300537&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40ctrl%2Ftinycolor%2Fdownload%2F%40ctrl%2Ftinycolor-3.1.7.tgz", + "integrity": "sha1-FYX2dimIIAKp+OFaKUHJpDIb+Aw=" + }, + "@develar/schema-utils": { + "version": "2.6.5", + "resolved": "https://registry.npm.taobao.org/@develar/schema-utils/download/@develar/schema-utils-2.6.5.tgz", + "integrity": "sha1-Ps4ixYOEAkGabgQl+FdCuWHZtsY=", + "dev": true, + "requires": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + } + }, + "@electron/get": { + "version": "1.12.2", + "resolved": "https://registry.npm.taobao.org/@electron/get/download/@electron/get-1.12.2.tgz", + "integrity": "sha1-ZEIGavuZvgjO+5ooHktGkrM3ZPM=", + "dev": true, + "requires": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "global-agent": "^2.0.2", + "global-tunnel-ng": "^2.7.1", + "got": "^9.6.0", + "progress": "^2.0.3", + "sanitize-filename": "^1.6.2", + "sumchecker": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-4.2.0.tgz?cache=0&sync_timestamp=1600502894812&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.2.0.tgz", + "integrity": "sha1-fxUPk5IOlMWPVXTC/QGjEQ7/5/E=", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npm.taobao.org/fs-extra/download/fs-extra-8.1.0.tgz", + "integrity": "sha1-SdQ8RaiM2Wd2aMt74bRu/bjS4cA=", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + } + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npm.taobao.org/@nodelib/fs.scandir/download/@nodelib/fs.scandir-2.1.3.tgz", + "integrity": "sha1-Olgr21OATGum0UZXnEblITDPSjs=", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/@nodelib/fs.stat/download/@nodelib/fs.stat-2.0.3.tgz", + "integrity": "sha1-NNxfTKu8cg9OYPdadH5+zWwXW9M=", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npm.taobao.org/@nodelib/fs.walk/download/@nodelib/fs.walk-1.2.4.tgz", + "integrity": "sha1-ARuSAqcKY2bkNspcBlhEUoqwSXY=", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + } + }, + "@npmcli/move-file": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/@npmcli/move-file/download/@npmcli/move-file-1.0.1.tgz", + "integrity": "sha1-3hAwcNrA9IzknPZpPCOvWcD3BGQ=", + "dev": true, + "requires": { + "mkdirp": "^1.0.4" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/mkdirp/download/mkdirp-1.0.4.tgz?cache=0&sync_timestamp=1600349118431&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmkdirp%2Fdownload%2Fmkdirp-1.0.4.tgz", + "integrity": "sha1-PrXtYmInVteaXw4qIh3+utdcL34=", + "dev": true + } + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npm.taobao.org/@sindresorhus/is/download/@sindresorhus/is-0.14.0.tgz", + "integrity": "sha1-n7OjzzEyMoFR81PeRjLgHlIQK+o=", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/@szmarczak/http-timer/download/@szmarczak/http-timer-1.1.2.tgz", + "integrity": "sha1-sWZeLEYaLNkvTBu/UNVFTeDUtCE=", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npm.taobao.org/@types/concat-stream/download/@types/concat-stream-1.6.0.tgz", + "integrity": "sha1-OU2+C7X+5Gs42JZzXoto7yOQ0A0=", + "requires": { + "@types/node": "*" + } + }, + "@types/d3-timer": { + "version": "1.0.10", + "resolved": "https://registry.npm.taobao.org/@types/d3-timer/download/@types/d3-timer-1.0.10.tgz?cache=0&sync_timestamp=1601417425052&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fd3-timer%2Fdownload%2F%40types%2Fd3-timer-1.0.10.tgz", + "integrity": "sha1-MpxRwskx9E7QrP94uMhFcazw7SE=" + }, + "@types/debug": { + "version": "4.1.5", + "resolved": "https://registry.npm.taobao.org/@types/debug/download/@types/debug-4.1.5.tgz", + "integrity": "sha1-sU76iFK3do2JiQZhPCP2iHE+As0=", + "dev": true + }, + "@types/electron-localshortcut": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/@types/electron-localshortcut/download/@types/electron-localshortcut-3.1.0.tgz", + "integrity": "sha1-6zwnC7R/HgtYN0nH6Yj1xcHn5KE=", + "dev": true, + "requires": { + "electron": "*" + } + }, + "@types/estree": { + "version": "0.0.45", + "resolved": "https://registry.npm.taobao.org/@types/estree/download/@types/estree-0.0.45.tgz", + "integrity": "sha1-6Th1cpmOXs2sIhlQ2rPow7Fq+IQ=" + }, + "@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npm.taobao.org/@types/form-data/download/@types/form-data-0.0.33.tgz", + "integrity": "sha1-yayFsqX9GENbjIXZ7LUObWyJP/g=", + "requires": { + "@types/node": "*" + } + }, + "@types/fs-extra": { + "version": "9.0.4", + "resolved": "https://registry.npm.taobao.org/@types/fs-extra/download/@types/fs-extra-9.0.4.tgz", + "integrity": "sha1-ElUxOM8EONuaMc3IsKOqkzLrZ6o=", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npm.taobao.org/@types/glob/download/@types/glob-7.1.3.tgz?cache=0&sync_timestamp=1596838206290&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fglob%2Fdownload%2F%40types%2Fglob-7.1.3.tgz", + "integrity": "sha1-5rqA82t9qtLGhazZJmOC5omFwYM=", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/json-schema": { + "version": "7.0.6", + "resolved": "https://registry.npm.taobao.org/@types/json-schema/download/@types/json-schema-7.0.6.tgz?cache=0&sync_timestamp=1598910403749&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fjson-schema%2Fdownload%2F%40types%2Fjson-schema-7.0.6.tgz", + "integrity": "sha1-9MfsQ+gbMZqYFRFQMXCfJph4kfA=", + "dev": true + }, + "@types/mdast": { + "version": "3.0.3", + "resolved": "https://registry.npm.taobao.org/@types/mdast/download/@types/mdast-3.0.3.tgz", + "integrity": "sha1-LX1nGxzR6j3rMG6nUDbCoEB9Les=", + "requires": { + "@types/unist": "*" + } + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npm.taobao.org/@types/minimatch/download/@types/minimatch-3.0.3.tgz?cache=0&sync_timestamp=1596839141589&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fminimatch%2Fdownload%2F%40types%2Fminimatch-3.0.3.tgz", + "integrity": "sha1-PcoOPzOyAPx9ETnAzZbBJoyt/Z0=", + "dev": true + }, + "@types/node": { + "version": "14.14.8", + "resolved": "https://registry.npm.taobao.org/@types/node/download/@types/node-14.14.8.tgz?cache=0&sync_timestamp=1605654639177&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.14.8.tgz", + "integrity": "sha1-ISe9gZSalci30yQPMlQ1LXJWOuw=" + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npm.taobao.org/@types/prop-types/download/@types/prop-types-15.7.3.tgz", + "integrity": "sha1-KrDV2i5YFflLC51LldHl8kOrLKc=" + }, + "@types/qs": { + "version": "6.9.5", + "resolved": "https://registry.npm.taobao.org/@types/qs/download/@types/qs-6.9.5.tgz?cache=0&sync_timestamp=1605055106687&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fqs%2Fdownload%2F%40types%2Fqs-6.9.5.tgz", + "integrity": "sha1-Q0cRvdSete5p2QwdZ8NUqajssYs=" + }, + "@types/react": { + "version": "16.9.51", + "resolved": "https://registry.npm.taobao.org/@types/react/download/@types/react-16.9.51.tgz", + "integrity": "sha1-+KpR/6mZbxOH9jaGaW2bWXE9K2A=", + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "16.9.8", + "resolved": "https://registry.npm.taobao.org/@types/react-dom/download/@types/react-dom-16.9.8.tgz", + "integrity": "sha1-/kweEd/GcVVzPfpqplEItJcctCM=", + "requires": { + "@types/react": "*" + } + }, + "@types/rimraf": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/@types/rimraf/download/@types/rimraf-3.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Frimraf%2Fdownload%2F%40types%2Frimraf-3.0.0.tgz", + "integrity": "sha1-udA/CQ7OJjZxiY1Xu3uwBwI6wZ8=", + "dev": true, + "requires": { + "@types/glob": "*", + "@types/node": "*" + } + }, + "@types/unist": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/@types/unist/download/@types/unist-2.0.3.tgz", + "integrity": "sha1-nAiGeYdvN061mD8VDUeHqm+zLX4=" + }, + "@types/yargs": { + "version": "15.0.9", + "resolved": "https://registry.npm.taobao.org/@types/yargs/download/@types/yargs-15.0.9.tgz", + "integrity": "sha1-UkzXmY/oEM2wLyYQG2mczNFW/xk=", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npm.taobao.org/@types/yargs-parser/download/@types/yargs-parser-15.0.0.tgz", + "integrity": "sha1-yz+fdBhp4gzOMw/765JxWQSDiC0=", + "dev": true + }, + "@types/yauzl": { + "version": "2.9.1", + "resolved": "https://registry.npm.taobao.org/@types/yauzl/download/@types/yauzl-2.9.1.tgz?cache=0&sync_timestamp=1596841684525&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fyauzl%2Fdownload%2F%40types%2Fyauzl-2.9.1.tgz", + "integrity": "sha1-0Q9p+fUi7vPPmOMK+2hKHh7JI68=", + "dev": true, + "optional": true, + "requires": { + "@types/node": "*" + } + }, + "@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/ast/download/@webassemblyjs/ast-1.9.0.tgz", + "integrity": "sha1-vYUGBLQEJFmlpBzX0zjL7Wle2WQ=", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/floating-point-hex-parser/download/@webassemblyjs/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha1-PD07Jxvd/ITesA9xNEQ4MR1S/7Q=", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/helper-api-error/download/@webassemblyjs/helper-api-error-1.9.0.tgz", + "integrity": "sha1-ID9nbjM7lsnaLuqzzO8zxFkotqI=", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/helper-buffer/download/@webassemblyjs/helper-buffer-1.9.0.tgz", + "integrity": "sha1-oUQtJpxf6yP8vJ73WdrDVH8p3gA=", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/helper-code-frame/download/@webassemblyjs/helper-code-frame-1.9.0.tgz", + "integrity": "sha1-ZH+Iks0gQ6gqwMjF51w28dkVnyc=", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/helper-fsm/download/@webassemblyjs/helper-fsm-1.9.0.tgz", + "integrity": "sha1-wFJWtxJEIUZx9LCOwQitY7cO3bg=", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/helper-module-context/download/@webassemblyjs/helper-module-context-1.9.0.tgz", + "integrity": "sha1-JdiIS3aDmHGgimxvgGw5ee9xLwc=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/helper-wasm-bytecode/download/@webassemblyjs/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha1-T+2L6sm4wU+MWLcNEk1UndH+V5A=", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/helper-wasm-section/download/@webassemblyjs/helper-wasm-section-1.9.0.tgz", + "integrity": "sha1-WkE41aYpK6GLBMWuSXF+QWeWU0Y=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/ieee754/download/@webassemblyjs/ieee754-1.9.0.tgz", + "integrity": "sha1-Fceg+6roP7JhQ7us9tbfFwKtOeQ=", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/leb128/download/@webassemblyjs/leb128-1.9.0.tgz", + "integrity": "sha1-8Zygt2ptxVYjoJz/p2noOPoeHJU=", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/utf8/download/@webassemblyjs/utf8-1.9.0.tgz", + "integrity": "sha1-BNM7Y2945qaBMifoJAL3Y3tiKas=", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/wasm-edit/download/@webassemblyjs/wasm-edit-1.9.0.tgz", + "integrity": "sha1-P+bXnT8PkiGDqoYALELdJWz+6c8=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/wasm-gen/download/@webassemblyjs/wasm-gen-1.9.0.tgz", + "integrity": "sha1-ULxw7Gje2OJ2OwGhQYv0NJGnpJw=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/wasm-opt/download/@webassemblyjs/wasm-opt-1.9.0.tgz", + "integrity": "sha1-IhEYHlsxMmRDzIES658LkChyGmE=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/wasm-parser/download/@webassemblyjs/wasm-parser-1.9.0.tgz", + "integrity": "sha1-nUjkSCbfSmWYKUqmyHRp1kL/9l4=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/wast-parser/download/@webassemblyjs/wast-parser-1.9.0.tgz", + "integrity": "sha1-MDERXXmsW9JhVWzsw/qQo+9FGRQ=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/@webassemblyjs/wast-printer/download/@webassemblyjs/wast-printer-1.9.0.tgz", + "integrity": "sha1-STXVTIX+9jewDOn1I3dFHQDUeJk=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "http://registry.npm.taobao.org/@xtuc/ieee754/download/@xtuc/ieee754-1.2.0.tgz", + "integrity": "sha1-7vAUoxRa5Hehy8AM0eVSM23Ot5A=", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "http://registry.npm.taobao.org/@xtuc/long/download/@xtuc/long-4.2.2.tgz", + "integrity": "sha1-0pHGpOl5ibXGHZrPOWrk/hM6cY0=", + "dev": true + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npm.taobao.org/abab/download/abab-2.0.5.tgz?cache=0&sync_timestamp=1599849993777&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fabab%2Fdownload%2Fabab-2.0.5.tgz", + "integrity": "sha1-wLZ4+zLWD8EhnHhNaoJv44Wut5o=", + "dev": true + }, + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npm.taobao.org/acorn/download/acorn-6.4.2.tgz?cache=0&sync_timestamp=1601885779818&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Facorn%2Fdownload%2Facorn-6.4.2.tgz", + "integrity": "sha1-NYZv1xBSjpLeEM8GAWSY5H454eY=", + "dev": true + }, + "add-dom-event-listener": { + "version": "1.1.0", + "resolved": "http://registry.npm.taobao.org/add-dom-event-listener/download/add-dom-event-listener-1.1.0.tgz", + "integrity": "sha1-apLbOg3Qq8JU4JXA8dwUrLuq4xA=", + "requires": { + "object-assign": "4.x" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/aggregate-error/download/aggregate-error-3.1.0.tgz", + "integrity": "sha1-kmcP9Q9TWb23o+DUDQ7DDFc3aHo=", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.5", + "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", + "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/ajv-errors/download/ajv-errors-1.0.1.tgz", + "integrity": "sha1-81mGrOuRr63sQQL72FAUlQzvpk0=", + "dev": true + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npm.taobao.org/ajv-keywords/download/ajv-keywords-3.5.2.tgz?cache=0&sync_timestamp=1595907068314&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv-keywords%2Fdownload%2Fajv-keywords-3.5.2.tgz", + "integrity": "sha1-MfKdpatuANHC0yms97WSlhTVAU0=", + "dev": true + }, + "ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/ansi-align/download/ansi-align-3.0.0.tgz", + "integrity": "sha1-tTazcc9ofKrvI2wY0+If43l0Z8s=", + "dev": true, + "requires": { + "string-width": "^3.0.0" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/ansi-regex/download/ansi-regex-4.1.0.tgz", + "integrity": "sha1-i5+PCM8ay4Q3Vqg5yox+MWjFGZc=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-3.2.1.tgz?cache=0&sync_timestamp=1601839122515&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-3.2.1.tgz", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "antd": { + "version": "4.6.6", + "resolved": "https://registry.npm.taobao.org/antd/download/antd-4.6.6.tgz?cache=0&sync_timestamp=1601187847495&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fantd%2Fdownload%2Fantd-4.6.6.tgz", + "integrity": "sha1-ebPiK6bd76opSNANhtLGEws1Yvs=", + "requires": { + "@ant-design/colors": "^4.0.5", + "@ant-design/css-animation": "^1.7.2", + "@ant-design/icons": "^4.2.1", + "@ant-design/react-slick": "~0.27.0", + "@babel/runtime": "^7.11.2", + "array-tree-filter": "^2.1.0", + "classnames": "^2.2.6", + "copy-to-clipboard": "^3.2.0", + "lodash": "^4.17.20", + "moment": "^2.25.3", + "omit.js": "^2.0.2", + "raf": "^3.4.1", + "rc-animate": "~3.1.0", + "rc-cascader": "~1.4.0", + "rc-checkbox": "~2.3.0", + "rc-collapse": "~2.0.0", + "rc-dialog": "~8.2.1", + "rc-drawer": "~4.1.0", + "rc-dropdown": "~3.2.0", + "rc-field-form": "~1.10.0", + "rc-image": "~3.0.6", + "rc-input-number": "~6.0.0", + "rc-mentions": "~1.5.0", + "rc-menu": "~8.7.1", + "rc-motion": "^2.0.0", + "rc-notification": "~4.4.0", + "rc-pagination": "~3.0.3", + "rc-picker": "~2.1.0", + "rc-progress": "~3.1.0", + "rc-rate": "~2.8.2", + "rc-resize-observer": "^0.2.3", + "rc-select": "~11.3.2", + "rc-slider": "~9.5.1", + "rc-steps": "~4.1.0", + "rc-switch": "~3.2.0", + "rc-table": "~7.9.2", + "rc-tabs": "~11.6.0", + "rc-textarea": "~0.3.0", + "rc-tooltip": "~5.0.0", + "rc-tree": "~3.10.0", + "rc-tree-select": "~4.1.1", + "rc-trigger": "~5.0.3", + "rc-upload": "~3.3.1", + "rc-util": "^5.1.0", + "scroll-into-view-if-needed": "^2.2.25", + "warning": "^4.0.3" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/anymatch/download/anymatch-3.1.1.tgz", + "integrity": "sha1-xV7PAhheJGklk5kxDBc84xIzsUI=", + "dev": true, + "optional": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "app-builder-bin": { + "version": "3.5.10", + "resolved": "https://registry.npm.taobao.org/app-builder-bin/download/app-builder-bin-3.5.10.tgz", + "integrity": "sha1-Sn+ZmfzMDENbYoSuE2a8dqF8Sn0=", + "dev": true + }, + "app-builder-lib": { + "version": "22.9.1", + "resolved": "https://registry.npm.taobao.org/app-builder-lib/download/app-builder-lib-22.9.1.tgz", + "integrity": "sha1-zLjxoCtihRSl36uUAfoql2aJQVw=", + "dev": true, + "requires": { + "7zip-bin": "~5.0.3", + "@develar/schema-utils": "~2.6.5", + "async-exit-hook": "^2.0.1", + "bluebird-lst": "^1.0.9", + "builder-util": "22.9.1", + "builder-util-runtime": "8.7.2", + "chromium-pickle-js": "^0.2.0", + "debug": "^4.3.0", + "ejs": "^3.1.5", + "electron-publish": "22.9.1", + "fs-extra": "^9.0.1", + "hosted-git-info": "^3.0.5", + "is-ci": "^2.0.0", + "isbinaryfile": "^4.0.6", + "js-yaml": "^3.14.0", + "lazy-val": "^1.0.4", + "minimatch": "^3.0.4", + "normalize-package-data": "^2.5.0", + "read-config-file": "6.0.0", + "sanitize-filename": "^1.6.3", + "semver": "^7.3.2", + "temp-file": "^3.3.7" + }, + "dependencies": { + "debug": { + "version": "4.3.0", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-4.3.0.tgz?cache=0&sync_timestamp=1600502826356&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.3.0.tgz", + "integrity": "sha1-76QcvxT8lEgHU2f9qt34I3baIR4=", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npm.taobao.org/fs-extra/download/fs-extra-9.0.1.tgz", + "integrity": "sha1-kQ2gBiQ3ukw5/t2GPxZ1zP78ufw=", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "3.0.7", + "resolved": "https://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-3.0.7.tgz", + "integrity": "sha1-owcnOF6oWs/O6U4KrZ42jHkuA2w=", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "isbinaryfile": { + "version": "4.0.6", + "resolved": "https://registry.npm.taobao.org/isbinaryfile/download/isbinaryfile-4.0.6.tgz", + "integrity": "sha1-7ctisiTitHEIMLZ0mMjk5aTSYQs=", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npm.taobao.org/jsonfile/download/jsonfile-6.1.0.tgz", + "integrity": "sha1-vFWyY0eTxnnsZAMJTrE2mKbsCq4=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/universalify/download/universalify-2.0.0.tgz", + "integrity": "sha1-daSYTv7cSwiXXFrrc/Uw0C3yVxc=", + "dev": true + } + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npm.taobao.org/lru-cache/download/lru-cache-6.0.0.tgz", + "integrity": "sha1-bW/mVw69lqr5D8rR2vo7JWbbOpQ=", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npm.taobao.org/semver/download/semver-7.3.2.tgz?cache=0&sync_timestamp=1599828351539&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-7.3.2.tgz", + "integrity": "sha1-YElisFK4HtB4aq6EOJ/7pw/9OTg=", + "dev": true + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/universalify/download/universalify-1.0.0.tgz", + "integrity": "sha1-thodoXPoQ1sv48Z9Kbmt+FlL0W0=", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/yallist/download/yallist-4.0.0.tgz", + "integrity": "sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=", + "dev": true + } + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/aproba/download/aproba-1.2.0.tgz", + "integrity": "sha1-aALmJk79GMeQobDVF/DyYnvyyUo=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npm.taobao.org/argparse/download/argparse-1.0.10.tgz", + "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + }, + "dependencies": { + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + } + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "http://registry.npm.taobao.org/arr-diff/download/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "http://registry.npm.taobao.org/arr-flatten/download/arr-flatten-1.1.0.tgz", + "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "http://registry.npm.taobao.org/arr-union/download/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-tree-filter": { + "version": "2.1.0", + "resolved": "http://registry.npm.taobao.org/array-tree-filter/download/array-tree-filter-2.1.0.tgz", + "integrity": "sha1-hzrAD+yDdJ8lWsjdCDgUtPYykZA=" + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/array-union/download/array-union-2.1.0.tgz", + "integrity": "sha1-t5hCCtvrHego2ErNii4j0+/oXo0=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "http://registry.npm.taobao.org/array-unique/download/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "http://registry.npm.taobao.org/asap/download/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "asar": { + "version": "3.0.3", + "resolved": "https://registry.npm.taobao.org/asar/download/asar-3.0.3.tgz", + "integrity": "sha1-H+8DwtbS3gy60Th4jk964DsSnHs=", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "chromium-pickle-js": "^0.2.0", + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "dependencies": { + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npm.taobao.org/commander/download/commander-5.1.0.tgz?cache=0&sync_timestamp=1598576116597&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcommander%2Fdownload%2Fcommander-5.1.0.tgz", + "integrity": "sha1-Rqu9FlL44Fm92u+Zu9yyrZzxea4=", + "dev": true + } + } + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npm.taobao.org/asn1.js/download/asn1.js-5.4.1.tgz", + "integrity": "sha1-EamAuE67kXgc41sP3C7ilON4Pwc=", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.11.9.tgz", + "integrity": "sha1-JtVWgpRY+dHoH8SJUkk9C6NQeCg=", + "dev": true + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npm.taobao.org/assert/download/assert-1.5.0.tgz", + "integrity": "sha1-VcEJqvbgrv2z3EtxJAxwv1dLGOs=", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.1.tgz?cache=0&sync_timestamp=1560975547815&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Finherits%2Fdownload%2Finherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npm.taobao.org/util/download/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/assign-symbols/download/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async": { + "version": "0.9.2", + "resolved": "https://registry.npm.taobao.org/async/download/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "dev": true + }, + "async-each": { + "version": "1.0.3", + "resolved": "http://registry.npm.taobao.org/async-each/download/async-each-1.0.3.tgz", + "integrity": "sha1-tyfb+H12UWAvBvTUrDh/R9kbDL8=", + "dev": true, + "optional": true + }, + "async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/async-exit-hook/download/async-exit-hook-2.0.1.tgz", + "integrity": "sha1-i9iwJLDsmxwBzMua+dspvXF9+vM=", + "dev": true + }, + "async-validator": { + "version": "3.4.0", + "resolved": "https://registry.npm.taobao.org/async-validator/download/async-validator-3.4.0.tgz?cache=0&sync_timestamp=1596623572478&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fasync-validator%2Fdownload%2Fasync-validator-3.4.0.tgz", + "integrity": "sha1-hxs+WUEkv0xOt7zRqeeLRPOwnK4=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "http://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/at-least-node/download/at-least-node-1.0.0.tgz", + "integrity": "sha1-YCzUtG6EStTv/JKoARo8RuAjjcI=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "http://registry.npm.taobao.org/atob/download/atob-2.1.2.tgz", + "integrity": "sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=", + "dev": true + }, + "author-regex": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/author-regex/download/author-regex-1.0.0.tgz", + "integrity": "sha1-0IiFvmubv5Q5/gh8dihyRfCoFFA=", + "dev": true + }, + "bail": { + "version": "1.0.5", + "resolved": "https://registry.npm.taobao.org/bail/download/bail-1.0.5.tgz", + "integrity": "sha1-tvoTNASjksvB+MS/Y/WVM1Hnp3Y=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "http://registry.npm.taobao.org/base/download/base-0.11.2.tgz", + "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/define-property/download/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/is-data-descriptor/download/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/is-descriptor/download/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npm.taobao.org/base64-js/download/base64-js-1.3.1.tgz", + "integrity": "sha1-WOzoy3XdB+ce0IxzarxfrE2/jfE=", + "dev": true + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npm.taobao.org/big.js/download/big.js-5.2.2.tgz", + "integrity": "sha1-ZfCvOC9Xi83HQr2cKB6cstd2gyg=", + "dev": true + }, + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/binary-extensions/download/binary-extensions-2.1.0.tgz", + "integrity": "sha1-MPpAyef+B9vIlWeM0ocCTeokHdk=", + "dev": true, + "optional": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npm.taobao.org/bindings/download/bindings-1.5.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbindings%2Fdownload%2Fbindings-1.5.0.tgz", + "integrity": "sha1-EDU8npRTNLwFEabZCzj7x8nFBN8=", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npm.taobao.org/bluebird/download/bluebird-3.7.2.tgz", + "integrity": "sha1-nyKcFb4nJFT/qXOs4NvueaGww28=", + "dev": true + }, + "bluebird-lst": { + "version": "1.0.9", + "resolved": "https://registry.npm.taobao.org/bluebird-lst/download/bluebird-lst-1.0.9.tgz", + "integrity": "sha1-pkoOQ2Vli5q1/odeud+2lBibtBw=", + "dev": true, + "requires": { + "bluebird": "^3.5.5" + } + }, + "bn.js": { + "version": "5.1.3", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-5.1.3.tgz", + "integrity": "sha1-vsoAVAj2Quvr6oCwQrTRjSrA7ms=", + "dev": true + }, + "boolean": { + "version": "3.0.1", + "resolved": "https://registry.npm.taobao.org/boolean/download/boolean-3.0.1.tgz", + "integrity": "sha1-NezytKLuGRsLRJhvFOtfBSpcu08=", + "dev": true + }, + "boxen": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/boxen/download/boxen-4.2.0.tgz", + "integrity": "sha1-5BG2I1fW1tNlh8isPV2XTaoHDmQ=", + "dev": true, + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "cli-boxes": "^2.2.0", + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npm.taobao.org/ansi-regex/download/ansi-regex-5.0.0.tgz", + "integrity": "sha1-OIU59VF5vzkznIGvMKZU1p+Hy3U=", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1601839226460&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz", + "integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-3.0.0.tgz", + "integrity": "sha1-P3PCv1JlkfV0zEksUeJFY0n4ROQ=", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz", + "integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz", + "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npm.taobao.org/emoji-regex/download/emoji-regex-8.0.0.tgz", + "integrity": "sha1-6Bj9ac5cz8tARZT4QpY79TFkzDc=", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-4.0.0.tgz", + "integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha1-8Rb4Bk/pCz94RKOJl8C3UFEmnx0=", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/string-width/download/string-width-4.2.0.tgz", + "integrity": "sha1-lSGCxGzHssMT0VluYjmSvRY7crU=", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-6.0.0.tgz?cache=0&sync_timestamp=1573280518303&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-6.0.0.tgz", + "integrity": "sha1-CxVx3XZpzNTz4G4U7x7tJiJa5TI=", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz?cache=0&sync_timestamp=1598611771865&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-7.2.0.tgz", + "integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npm.taobao.org/type-fest/download/type-fest-0.8.1.tgz?cache=0&sync_timestamp=1605191548126&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftype-fest%2Fdownload%2Ftype-fest-0.8.1.tgz", + "integrity": "sha1-CeJJ696FHTseSNJ8EFREZn8XuD0=", + "dev": true + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npm.taobao.org/brace-expansion/download/brace-expansion-1.1.11.tgz?cache=0&sync_timestamp=1601898189928&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbrace-expansion%2Fdownload%2Fbrace-expansion-1.1.11.tgz", + "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "http://registry.npm.taobao.org/braces/download/braces-2.3.2.tgz", + "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "http://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "http://registry.npm.taobao.org/brorand/download/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "http://registry.npm.taobao.org/browserify-aes/download/browserify-aes-1.2.0.tgz", + "integrity": "sha1-Mmc0ZC9APavDADIJhTu3CtQo70g=", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/browserify-cipher/download/browserify-cipher-1.0.1.tgz", + "integrity": "sha1-jWR0wbhwv9q807z8wZNKEOlPFfA=", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/browserify-des/download/browserify-des-1.0.2.tgz", + "integrity": "sha1-OvTx9Zg5QDVy8cZiBDdfen9wPpw=", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "http://registry.npm.taobao.org/browserify-rsa/download/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.11.9.tgz", + "integrity": "sha1-JtVWgpRY+dHoH8SJUkk9C6NQeCg=", + "dev": true + } + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npm.taobao.org/browserify-sign/download/browserify-sign-4.2.1.tgz", + "integrity": "sha1-6vSt1G3VS+O7OzbAzxWrvrp5VsM=", + "dev": true, + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npm.taobao.org/readable-stream/download/readable-stream-3.6.0.tgz?cache=0&sync_timestamp=1581624324274&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freadable-stream%2Fdownload%2Freadable-stream-3.6.0.tgz", + "integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.2.1.tgz", + "integrity": "sha1-Hq+fqb2x/dTsdfWPnNtOa3gn7sY=", + "dev": true + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "http://registry.npm.taobao.org/browserify-zlib/download/browserify-zlib-0.2.0.tgz", + "integrity": "sha1-KGlFnZqjviRf6P4sofRuLn9U1z8=", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npm.taobao.org/buffer/download/buffer-4.9.2.tgz?cache=0&sync_timestamp=1588706716358&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbuffer%2Fdownload%2Fbuffer-4.9.2.tgz", + "integrity": "sha1-Iw6tNEACmIZEhBqwJEr4xEu+Pvg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "http://registry.npm.taobao.org/buffer-alloc/download/buffer-alloc-1.2.0.tgz", + "integrity": "sha1-iQ3ZDZI6hz4I4Q5f1RpX5bfM4Ow=", + "dev": true, + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "http://registry.npm.taobao.org/buffer-alloc-unsafe/download/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha1-vX3CauKXLQ7aJTvgYdupkjScGfA=", + "dev": true + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "http://registry.npm.taobao.org/buffer-crc32/download/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/buffer-fill/download/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "http://registry.npm.taobao.org/buffer-from/download/buffer-from-1.1.1.tgz", + "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "http://registry.npm.taobao.org/buffer-xor/download/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builder-util": { + "version": "22.9.1", + "resolved": "https://registry.npm.taobao.org/builder-util/download/builder-util-22.9.1.tgz", + "integrity": "sha1-twh6XN5Hf5DXGMpdf6+2riYbFq8=", + "dev": true, + "requires": { + "7zip-bin": "~5.0.3", + "@types/debug": "^4.1.5", + "@types/fs-extra": "^9.0.1", + "app-builder-bin": "3.5.10", + "bluebird-lst": "^1.0.9", + "builder-util-runtime": "8.7.2", + "chalk": "^4.1.0", + "debug": "^4.3.0", + "fs-extra": "^9.0.1", + "is-ci": "^2.0.0", + "js-yaml": "^3.14.0", + "source-map-support": "^0.5.19", + "stat-mode": "^1.0.0", + "temp-file": "^3.3.7" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1601839226460&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz", + "integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-4.1.0.tgz", + "integrity": "sha1-ThSHCmGNni7dl92DRf2dncMVZGo=", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz", + "integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz", + "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=", + "dev": true + }, + "debug": { + "version": "4.3.0", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-4.3.0.tgz?cache=0&sync_timestamp=1600502826356&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.3.0.tgz", + "integrity": "sha1-76QcvxT8lEgHU2f9qt34I3baIR4=", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npm.taobao.org/fs-extra/download/fs-extra-9.0.1.tgz", + "integrity": "sha1-kQ2gBiQ3ukw5/t2GPxZ1zP78ufw=", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-4.0.0.tgz", + "integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npm.taobao.org/jsonfile/download/jsonfile-6.1.0.tgz", + "integrity": "sha1-vFWyY0eTxnnsZAMJTrE2mKbsCq4=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/universalify/download/universalify-2.0.0.tgz", + "integrity": "sha1-daSYTv7cSwiXXFrrc/Uw0C3yVxc=", + "dev": true + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz?cache=0&sync_timestamp=1598611771865&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-7.2.0.tgz", + "integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/universalify/download/universalify-1.0.0.tgz", + "integrity": "sha1-thodoXPoQ1sv48Z9Kbmt+FlL0W0=", + "dev": true + } + } + }, + "builder-util-runtime": { + "version": "8.7.2", + "resolved": "https://registry.npm.taobao.org/builder-util-runtime/download/builder-util-runtime-8.7.2.tgz", + "integrity": "sha1-2Tr8cUKKEnibQ34ThQ4fp9qVbXI=", + "dev": true, + "requires": { + "debug": "^4.1.1", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-4.2.0.tgz?cache=0&sync_timestamp=1600502826356&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.2.0.tgz", + "integrity": "sha1-fxUPk5IOlMWPVXTC/QGjEQ7/5/E=", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + } + } + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "http://registry.npm.taobao.org/builtin-status-codes/download/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npm.taobao.org/cacache/download/cacache-12.0.4.tgz", + "integrity": "sha1-ZovL0QWutfHZL+JVcOyVJcj6pAw=", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npm.taobao.org/rimraf/download/rimraf-2.7.1.tgz?cache=0&sync_timestamp=1581257110269&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frimraf%2Fdownload%2Frimraf-2.7.1.tgz", + "integrity": "sha1-NXl/E6f9rcVmFCwp1PB8ytSD4+w=", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/cache-base/download/cache-base-1.0.1.tgz", + "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npm.taobao.org/cacheable-request/download/cacheable-request-6.1.0.tgz", + "integrity": "sha1-IP+4vRYrpL4R6VZ9gj22UQUsqRI=", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npm.taobao.org/get-stream/download/get-stream-5.2.0.tgz?cache=0&sync_timestamp=1597056502934&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fget-stream%2Fdownload%2Fget-stream-5.2.0.tgz", + "integrity": "sha1-SWaheV7lrOZecGxLe+txJX1uItM=", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/lowercase-keys/download/lowercase-keys-2.0.0.tgz", + "integrity": "sha1-JgPni3tLAAbLyi+8yKMgJVislHk=", + "dev": true + } + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npm.taobao.org/camelcase/download/camelcase-5.3.1.tgz?cache=0&sync_timestamp=1589682790492&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcamelcase%2Fdownload%2Fcamelcase-5.3.1.tgz", + "integrity": "sha1-48mzFWnhBoEd8kL3FXJaH0xJQyA=", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "http://registry.npm.taobao.org/caseless/download/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-2.4.2.tgz?cache=0&sync_timestamp=1591687070184&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchalk%2Fdownload%2Fchalk-2.4.2.tgz", + "integrity": "sha1-zUJUFnelQzPPVBpJEIwUMrRMlCQ=", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npm.taobao.org/character-entities/download/character-entities-1.2.4.tgz", + "integrity": "sha1-4Sw5Obfq9OWxXnrUxeKOHUjFsWs=" + }, + "character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npm.taobao.org/character-entities-legacy/download/character-entities-legacy-1.1.4.tgz", + "integrity": "sha1-lLwYRdznClu50uzHSHJWYSk9j8E=" + }, + "character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npm.taobao.org/character-reference-invalid/download/character-reference-invalid-1.1.4.tgz", + "integrity": "sha1-CDMpzaDq4nKrPbvzfpo4LBOvFWA=" + }, + "chokidar": { + "version": "3.4.2", + "resolved": "https://registry.npm.taobao.org/chokidar/download/chokidar-3.4.2.tgz", + "integrity": "sha1-ONyOZY3sOAl0HrPve7Ckf+QkIy0=", + "dev": true, + "optional": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.4.0" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "http://registry.npm.taobao.org/braces/download/braces-3.0.2.tgz", + "integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=", + "dev": true, + "optional": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "http://registry.npm.taobao.org/fill-range/download/fill-range-7.0.1.tgz", + "integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=", + "dev": true, + "optional": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "http://registry.npm.taobao.org/is-number/download/is-number-7.0.0.tgz", + "integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=", + "dev": true, + "optional": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "http://registry.npm.taobao.org/to-regex-range/download/to-regex-range-5.0.1.tgz", + "integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=", + "dev": true, + "optional": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npm.taobao.org/chownr/download/chownr-1.1.4.tgz", + "integrity": "sha1-b8nXtC0ypYNZYzdmbn0ICE2izGs=", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/chrome-trace-event/download/chrome-trace-event-1.0.2.tgz", + "integrity": "sha1-I0CQ7pfH1K0aLEvq4nUF3v/GCKQ=", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "chromium-pickle-js": { + "version": "0.2.0", + "resolved": "http://registry.npm.taobao.org/chromium-pickle-js/download/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha1-BKEGZywYsIWrd02YPfo+oTjyIgU=", + "dev": true + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/ci-info/download/ci-info-2.0.0.tgz", + "integrity": "sha1-Z6npZL4xpR4V5QENWObxKDQAL0Y=", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "http://registry.npm.taobao.org/cipher-base/download/cipher-base-1.0.4.tgz", + "integrity": "sha1-h2Dk7MJy9MNjUy+SbYdKriwTl94=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "http://registry.npm.taobao.org/class-utils/download/class-utils-0.3.6.tgz", + "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "http://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "classnames": { + "version": "2.2.6", + "resolved": "http://registry.npm.taobao.org/classnames/download/classnames-2.2.6.tgz", + "integrity": "sha1-Q5Nb/90pHzJtrQogUwmzjQD2UM4=" + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/clean-stack/download/clean-stack-2.2.0.tgz", + "integrity": "sha1-7oRy27Ep5yezHooQpCfe6d/kAIs=", + "dev": true + }, + "cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/cli-boxes/download/cli-boxes-2.2.1.tgz", + "integrity": "sha1-3dUDXSUJT84iDpyrQKRYQKRAMY8=", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npm.taobao.org/cliui/download/cliui-5.0.0.tgz?cache=0&sync_timestamp=1597608006561&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcliui%2Fdownload%2Fcliui-5.0.0.tgz", + "integrity": "sha1-3u/P2y6AB4SqNPRvoI4GhRx7u8U=", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "clone-response": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/clone-response/download/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/collection-visit/download/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-1.9.3.tgz?cache=0&sync_timestamp=1566248870121&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcolor-convert%2Fdownload%2Fcolor-convert-1.9.3.tgz", + "integrity": "sha1-u3GFBpDh8TZWfeYp0tVHHe2kweg=", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "http://registry.npm.taobao.org/color-name/download/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npm.taobao.org/combined-stream/download/combined-stream-1.0.8.tgz", + "integrity": "sha1-w9RaizT9cwYxoRCoolIGgrMdWn8=", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npm.taobao.org/commander/download/commander-2.20.3.tgz?cache=0&sync_timestamp=1598576116597&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcommander%2Fdownload%2Fcommander-2.20.3.tgz", + "integrity": "sha1-/UhehMA+tIgcIHIrpIA16FMa6zM=", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/commondir/download/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npm.taobao.org/compare-version/download/compare-version-0.1.2.tgz", + "integrity": "sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "http://registry.npm.taobao.org/component-emitter/download/component-emitter-1.3.0.tgz", + "integrity": "sha1-FuQHD7qK4ptnnyIVhT7hgasuq8A=", + "dev": true + }, + "compute-scroll-into-view": { + "version": "1.0.16", + "resolved": "https://registry.npm.taobao.org/compute-scroll-into-view/download/compute-scroll-into-view-1.0.16.tgz?cache=0&sync_timestamp=1599054579209&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcompute-scroll-into-view%2Fdownload%2Fcompute-scroll-into-view-1.0.16.tgz", + "integrity": "sha1-W3v09xJ+osGbdQNT185ndqkO4Ig=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "http://registry.npm.taobao.org/concat-map/download/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "http://registry.npm.taobao.org/concat-stream/download/concat-stream-1.6.2.tgz", + "integrity": "sha1-kEvfGUzTEi/Gdcd/xKw9T/D9GjQ=", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "config-chain": { + "version": "1.1.12", + "resolved": "http://registry.npm.taobao.org/config-chain/download/config-chain-1.1.12.tgz", + "integrity": "sha1-D96NCRIA616AjK8l/mGMAvSOTvo=", + "dev": true, + "optional": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npm.taobao.org/configstore/download/configstore-5.0.1.tgz", + "integrity": "sha1-02UCG130uYzdGH1qOw4/anzF7ZY=", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/make-dir/download/make-dir-3.1.0.tgz", + "integrity": "sha1-QV6WcEazp/HRhSd9hKpYIDcmoT8=", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npm.taobao.org/semver/download/semver-6.3.0.tgz?cache=0&sync_timestamp=1599828351539&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-6.3.0.tgz", + "integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=", + "dev": true + } + } + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/console-browserify/download/console-browserify-1.2.0.tgz", + "integrity": "sha1-ZwY871fOts9Jk6KrOlWECujEkzY=", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/constants-browserify/download/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "http://registry.npm.taobao.org/copy-concurrently/download/copy-concurrently-1.0.5.tgz", + "integrity": "sha1-kilzmMrjSTf8r9bsgTnBgFHwteA=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npm.taobao.org/rimraf/download/rimraf-2.7.1.tgz?cache=0&sync_timestamp=1581257110269&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frimraf%2Fdownload%2Frimraf-2.7.1.tgz", + "integrity": "sha1-NXl/E6f9rcVmFCwp1PB8ytSD4+w=", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "http://registry.npm.taobao.org/copy-descriptor/download/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-to-clipboard": { + "version": "3.3.1", + "resolved": "https://registry.npm.taobao.org/copy-to-clipboard/download/copy-to-clipboard-3.3.1.tgz", + "integrity": "sha1-EVqhqZmP+rYZb5MHatbaO5E2Yq4=", + "requires": { + "toggle-selection": "^1.0.6" + } + }, + "copy-webpack-plugin": { + "version": "6.3.1", + "resolved": "https://registry.npm.taobao.org/copy-webpack-plugin/download/copy-webpack-plugin-6.3.1.tgz?cache=0&sync_timestamp=1605282119107&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcopy-webpack-plugin%2Fdownload%2Fcopy-webpack-plugin-6.3.1.tgz", + "integrity": "sha1-zrbpw+SRDmOndP1KJ0URVndfbio=", + "dev": true, + "requires": { + "cacache": "^15.0.5", + "fast-glob": "^3.2.4", + "find-cache-dir": "^3.3.1", + "glob-parent": "^5.1.1", + "globby": "^11.0.1", + "loader-utils": "^2.0.0", + "normalize-path": "^3.0.0", + "p-limit": "^3.0.2", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1", + "webpack-sources": "^1.4.3" + }, + "dependencies": { + "cacache": { + "version": "15.0.5", + "resolved": "https://registry.npm.taobao.org/cacache/download/cacache-15.0.5.tgz", + "integrity": "sha1-aRYoM9opFw1nMjNGQ8YOAF9fF9A=", + "dev": true, + "requires": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.0", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/chownr/download/chownr-2.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchownr%2Fdownload%2Fchownr-2.0.0.tgz", + "integrity": "sha1-Fb++U9LqtM9w8YqM1o6+Wzyx3s4=", + "dev": true + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npm.taobao.org/find-cache-dir/download/find-cache-dir-3.3.1.tgz", + "integrity": "sha1-ibM/rUpGcNqpT4Vff74x1thP6IA=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/find-up/download/find-up-4.1.0.tgz", + "integrity": "sha1-l6/n1s3AvFkoWEt8jXsW6KmqXRk=", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npm.taobao.org/json5/download/json5-2.1.3.tgz", + "integrity": "sha1-ybD3+pIzv+WAf+ZvzzpWF+1ZfUM=", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-2.0.0.tgz", + "integrity": "sha1-5MrOW4FtQloWa18JfhDNErNgZLA=", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npm.taobao.org/locate-path/download/locate-path-5.0.0.tgz", + "integrity": "sha1-Gvujlq/WdqbUJQTQpno6frn2KqA=", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npm.taobao.org/lru-cache/download/lru-cache-6.0.0.tgz", + "integrity": "sha1-bW/mVw69lqr5D8rR2vo7JWbbOpQ=", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/make-dir/download/make-dir-3.1.0.tgz", + "integrity": "sha1-QV6WcEazp/HRhSd9hKpYIDcmoT8=", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/mkdirp/download/mkdirp-1.0.4.tgz?cache=0&sync_timestamp=1600349118431&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmkdirp%2Fdownload%2Fmkdirp-1.0.4.tgz", + "integrity": "sha1-PrXtYmInVteaXw4qIh3+utdcL34=", + "dev": true + }, + "p-limit": { + "version": "3.0.2", + "resolved": "https://registry.npm.taobao.org/p-limit/download/p-limit-3.0.2.tgz", + "integrity": "sha1-FmTgEK88rcaBuq/T4qQ3vnsPtf4=", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/p-locate/download/p-locate-4.1.0.tgz", + "integrity": "sha1-o0KLtwiLOmApL2aRkni3wpetTwc=", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + }, + "dependencies": { + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npm.taobao.org/p-limit/download/p-limit-2.3.0.tgz", + "integrity": "sha1-PdM8ZHohT9//2DWTPrCG2g3CHbE=", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + } + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/path-exists/download/path-exists-4.0.0.tgz", + "integrity": "sha1-UTvb4tO5XXdi6METfvoZXGxhtbM=", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/pkg-dir/download/pkg-dir-4.2.0.tgz?cache=0&sync_timestamp=1602859010405&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpkg-dir%2Fdownload%2Fpkg-dir-4.2.0.tgz", + "integrity": "sha1-8JkTPfft5CLoHR2ESCcO6z5CYfM=", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-3.0.0.tgz", + "integrity": "sha1-Z1AvaqK2ai1AMrQnmilEl4oJE+8=", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npm.taobao.org/semver/download/semver-6.3.0.tgz?cache=0&sync_timestamp=1599828351539&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-6.3.0.tgz", + "integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=", + "dev": true + }, + "serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npm.taobao.org/serialize-javascript/download/serialize-javascript-5.0.1.tgz", + "integrity": "sha1-eIbshIBJpGJGepfT2Rjrsqr5NPQ=", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "ssri": { + "version": "8.0.0", + "resolved": "https://registry.npm.taobao.org/ssri/download/ssri-8.0.0.tgz", + "integrity": "sha1-ecp04h+M6u3fy0uQFDxFi42YiAg=", + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/yallist/download/yallist-4.0.0.tgz", + "integrity": "sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=", + "dev": true + } + } + }, + "core-js": { + "version": "3.6.5", + "resolved": "https://registry.npm.taobao.org/core-js/download/core-js-3.6.5.tgz?cache=0&sync_timestamp=1589682726446&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcore-js%2Fdownload%2Fcore-js-3.6.5.tgz", + "integrity": "sha1-c5XcJzrzf7LlDpvT2f6EEoUjHRo=", + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/core-util-is/download/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npm.taobao.org/create-ecdh/download/create-ecdh-4.0.4.tgz", + "integrity": "sha1-1uf0v/pmc2CFoHYv06YyaE2rzE4=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.11.9.tgz", + "integrity": "sha1-JtVWgpRY+dHoH8SJUkk9C6NQeCg=", + "dev": true + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "http://registry.npm.taobao.org/create-hash/download/create-hash-1.2.0.tgz", + "integrity": "sha1-iJB4rxGmN1a8+1m9IhmWvjqe8ZY=", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "http://registry.npm.taobao.org/create-hmac/download/create-hmac-1.1.7.tgz", + "integrity": "sha1-aRcMeLOrlXFHsriwRXLkfq0iQ/8=", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "create-react-context": { + "version": "0.2.3", + "resolved": "https://registry.npm.taobao.org/create-react-context/download/create-react-context-0.2.3.tgz", + "integrity": "sha1-nsFAppFKIu8EuLCbd3HeiVZ8tvM=", + "requires": { + "fbjs": "^0.8.0", + "gud": "^1.0.0" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npm.taobao.org/cross-spawn/download/cross-spawn-6.0.5.tgz", + "integrity": "sha1-Sl7Hxk364iw6FBJNus3uhG2Ay8Q=", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "http://registry.npm.taobao.org/crypto-browserify/download/crypto-browserify-3.12.0.tgz", + "integrity": "sha1-OWz58xN/A+S45TLFj2mCVOAPgOw=", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/crypto-random-string/download/crypto-random-string-2.0.0.tgz", + "integrity": "sha1-7yp6lm7BEIM4g2m6oC6+rSKbMNU=", + "dev": true + }, + "css-loader": { + "version": "4.3.0", + "resolved": "https://registry.npm.taobao.org/css-loader/download/css-loader-4.3.0.tgz", + "integrity": "sha1-yIivZLKlsuhUYscsD0qFx+Lggh4=", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^2.0.0", + "postcss": "^7.0.32", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.3", + "postcss-modules-scope": "^2.2.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^2.7.1", + "semver": "^7.3.2" + }, + "dependencies": { + "camelcase": { + "version": "6.0.0", + "resolved": "https://registry.npm.taobao.org/camelcase/download/camelcase-6.0.0.tgz?cache=0&sync_timestamp=1589682790492&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcamelcase%2Fdownload%2Fcamelcase-6.0.0.tgz", + "integrity": "sha1-Uln3ww414njxvcKk2RIws3ytmB4=", + "dev": true + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npm.taobao.org/json5/download/json5-2.1.3.tgz", + "integrity": "sha1-ybD3+pIzv+WAf+ZvzzpWF+1ZfUM=", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-2.0.0.tgz", + "integrity": "sha1-5MrOW4FtQloWa18JfhDNErNgZLA=", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-2.7.1.tgz", + "integrity": "sha1-HKTzLRskxZDCA7jnpQvw6kzTlNc=", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npm.taobao.org/semver/download/semver-7.3.2.tgz", + "integrity": "sha1-YElisFK4HtB4aq6EOJ/7pw/9OTg=", + "dev": true + } + } + }, + "cssesc": { + "version": "3.0.0", + "resolved": "http://registry.npm.taobao.org/cssesc/download/cssesc-3.0.0.tgz", + "integrity": "sha1-N3QZGZA7hoVl4cCep0dEXNGJg+4=", + "dev": true + }, + "csstype": { + "version": "3.0.3", + "resolved": "https://registry.npm.taobao.org/csstype/download/csstype-3.0.3.tgz?cache=0&sync_timestamp=1598350086358&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcsstype%2Fdownload%2Fcsstype-3.0.3.tgz", + "integrity": "sha1-K0ELvro4upYzNTr/NLBdl1XQZfg=" + }, + "cyclist": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/cyclist/download/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "d3-color": { + "version": "1.4.1", + "resolved": "https://registry.npm.taobao.org/d3-color/download/d3-color-1.4.1.tgz", + "integrity": "sha1-xSACv4hGraRCTVXZeYL+8m6zvIo=" + }, + "d3-dispatch": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/d3-dispatch/download/d3-dispatch-2.0.0.tgz", + "integrity": "sha1-ihjhb3bdP8rvQhY8l7kmqptV588=" + }, + "d3-ease": { + "version": "1.0.7", + "resolved": "https://registry.npm.taobao.org/d3-ease/download/d3-ease-1.0.7.tgz", + "integrity": "sha1-moNIkO+LiujFWLL+Vb1X9Zk7heI=" + }, + "d3-force": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/d3-force/download/d3-force-2.1.1.tgz?cache=0&sync_timestamp=1598222489832&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fd3-force%2Fdownload%2Fd3-force-2.1.1.tgz", + "integrity": "sha1-8gzL8ebJ6ArdGSbwm1H2hqi8CTc=", + "requires": { + "d3-dispatch": "1 - 2", + "d3-quadtree": "1 - 2", + "d3-timer": "1 - 2" + } + }, + "d3-interpolate": { + "version": "1.4.0", + "resolved": "https://registry.npm.taobao.org/d3-interpolate/download/d3-interpolate-1.4.0.tgz?cache=0&sync_timestamp=1598193861487&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fd3-interpolate%2Fdownload%2Fd3-interpolate-1.4.0.tgz", + "integrity": "sha1-Um554tgNqjg/ngwcHH3MDwWD6Yc=", + "requires": { + "d3-color": "1" + } + }, + "d3-quadtree": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/d3-quadtree/download/d3-quadtree-2.0.0.tgz?cache=0&sync_timestamp=1598194930763&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fd3-quadtree%2Fdownload%2Fd3-quadtree-2.0.0.tgz", + "integrity": "sha1-7brQRc74hwH2/uOu6Ok/szLTD50=" + }, + "d3-timer": { + "version": "1.0.10", + "resolved": "https://registry.npm.taobao.org/d3-timer/download/d3-timer-1.0.10.tgz?cache=0&sync_timestamp=1598192644493&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fd3-timer%2Fdownload%2Fd3-timer-1.0.10.tgz", + "integrity": "sha1-3+dripF0iDGxO22ceT/71QjdneU=" + }, + "dagre": { + "version": "0.8.5", + "resolved": "https://registry.npm.taobao.org/dagre/download/dagre-0.8.5.tgz", + "integrity": "sha1-ujCwBV2sErbB/MJHgXRCd30Gr+4=", + "requires": { + "graphlib": "^2.1.8", + "lodash": "^4.17.15" + } + }, + "date-fns": { + "version": "2.16.1", + "resolved": "https://registry.npm.taobao.org/date-fns/download/date-fns-2.16.1.tgz", + "integrity": "sha1-BXdXksPzMx2oEq8lPhqTWFHTg0s=" + }, + "dayjs": { + "version": "1.9.1", + "resolved": "https://registry.npm.taobao.org/dayjs/download/dayjs-1.9.1.tgz?cache=0&sync_timestamp=1601296698962&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdayjs%2Fdownload%2Fdayjs-1.9.1.tgz", + "integrity": "sha1-IBp1X321ED7W3mO6k6mEFBx1RUE=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1600502894812&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/decamelize/download/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "http://registry.npm.taobao.org/decode-uri-component/download/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npm.taobao.org/decompress-response/download/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "deep-eql": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/deep-eql/download/deep-eql-4.0.0.tgz", + "integrity": "sha1-xwrycTpOGNnCwSA/+dEau9Ucj70=", + "requires": { + "type-detect": "^4.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npm.taobao.org/deep-extend/download/deep-extend-0.6.0.tgz", + "integrity": "sha1-xPp8lUBKF6nD6Mp+FTcxK3NjMKw=", + "dev": true + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/defer-to-connect/download/defer-to-connect-1.1.3.tgz", + "integrity": "sha1-MxrgUMCNz3ifjIOnuB8O2U9KxZE=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "http://registry.npm.taobao.org/define-properties/download/define-properties-1.1.3.tgz", + "integrity": "sha1-z4jabL7ib+bbcJT2HYcMvYTO6fE=", + "dev": true, + "optional": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "http://registry.npm.taobao.org/define-property/download/define-property-2.0.2.tgz", + "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/is-data-descriptor/download/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/is-descriptor/download/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/delayed-stream/download/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/des.js/download/des.js-1.0.1.tgz", + "integrity": "sha1-U4IULhvcU/hdhtU+X0qn3rkeCEM=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "detect-browser": { + "version": "5.2.0", + "resolved": "https://registry.npm.taobao.org/detect-browser/download/detect-browser-5.2.0.tgz?cache=0&sync_timestamp=1602161052538&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdetect-browser%2Fdownload%2Fdetect-browser-5.2.0.tgz", + "integrity": "sha1-yc1a+pamoZ/aC76em+SKa24enJc=" + }, + "detect-file": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/detect-file/download/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, + "detect-node": { + "version": "2.0.4", + "resolved": "http://registry.npm.taobao.org/detect-node/download/detect-node-2.0.4.tgz", + "integrity": "sha1-AU7o+PZpxcWAI9pkuBecCDooxGw=", + "dev": true, + "optional": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "http://registry.npm.taobao.org/diffie-hellman/download/diffie-hellman-5.0.3.tgz", + "integrity": "sha1-QOjumPVaIUlgcUaSHGPhrl89KHU=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.11.9.tgz", + "integrity": "sha1-JtVWgpRY+dHoH8SJUkk9C6NQeCg=", + "dev": true + } + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npm.taobao.org/dir-glob/download/dir-glob-3.0.1.tgz", + "integrity": "sha1-Vtv3PZkqSpO6FYT0U0Bj/S5BcX8=", + "dev": true, + "requires": { + "path-type": "^4.0.0" + }, + "dependencies": { + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/path-type/download/path-type-4.0.0.tgz", + "integrity": "sha1-hO0BwKe6OAr+CdkKjBgNzZ0DBDs=", + "dev": true + } + } + }, + "dmg-builder": { + "version": "22.9.1", + "resolved": "https://registry.npm.taobao.org/dmg-builder/download/dmg-builder-22.9.1.tgz", + "integrity": "sha1-ZGRyJPN+5H/JvQGUfCHMAQowUR8=", + "dev": true, + "requires": { + "app-builder-lib": "22.9.1", + "builder-util": "22.9.1", + "fs-extra": "^9.0.1", + "iconv-lite": "^0.6.2", + "js-yaml": "^3.14.0", + "sanitize-filename": "^1.6.3" + }, + "dependencies": { + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npm.taobao.org/fs-extra/download/fs-extra-9.0.1.tgz", + "integrity": "sha1-kQ2gBiQ3ukw5/t2GPxZ1zP78ufw=", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npm.taobao.org/jsonfile/download/jsonfile-6.1.0.tgz", + "integrity": "sha1-vFWyY0eTxnnsZAMJTrE2mKbsCq4=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/universalify/download/universalify-2.0.0.tgz", + "integrity": "sha1-daSYTv7cSwiXXFrrc/Uw0C3yVxc=", + "dev": true + } + } + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/universalify/download/universalify-1.0.0.tgz", + "integrity": "sha1-thodoXPoQ1sv48Z9Kbmt+FlL0W0=", + "dev": true + } + } + }, + "dom-align": { + "version": "1.12.0", + "resolved": "https://registry.npm.taobao.org/dom-align/download/dom-align-1.12.0.tgz", + "integrity": "sha1-VvtxVt8LkQmYMDZNLUj4iWP1opw=" + }, + "dom-serializer": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/dom-serializer/download/dom-serializer-1.1.0.tgz", + "integrity": "sha1-X3yCjxv8RIh9wqMVq1xFaR1US1g=", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0", + "entities": "^2.0.0" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/domain-browser/download/domain-browser-1.2.0.tgz?cache=0&sync_timestamp=1599393068432&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdomain-browser%2Fdownload%2Fdomain-browser-1.2.0.tgz", + "integrity": "sha1-PTH1AZGmdJ3RN1p/Ui6CPULlTto=", + "dev": true + }, + "domelementtype": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/domelementtype/download/domelementtype-2.0.2.tgz?cache=0&sync_timestamp=1600028495728&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdomelementtype%2Fdownload%2Fdomelementtype-2.0.2.tgz", + "integrity": "sha1-87blSSAeRvWItZRj3XcYcTH+aXE=" + }, + "domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npm.taobao.org/domhandler/download/domhandler-3.3.0.tgz", + "integrity": "sha1-bbfqRuRhfrFc+HXfaLK4UkzgA3o=", + "requires": { + "domelementtype": "^2.0.1" + } + }, + "domutils": { + "version": "2.4.2", + "resolved": "https://registry.npm.taobao.org/domutils/download/domutils-2.4.2.tgz", + "integrity": "sha1-fuW+JhlE4a1IfZqgYWcgAQEjkis=", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.0.1", + "domhandler": "^3.3.0" + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npm.taobao.org/dot-prop/download/dot-prop-5.3.0.tgz", + "integrity": "sha1-kMzOcIzZzYLMTcjD3dmr3VWyDog=", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npm.taobao.org/dotenv/download/dotenv-8.2.0.tgz", + "integrity": "sha1-l+YZJZradQ7qPk6j4mvO6lQksWo=", + "dev": true + }, + "dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npm.taobao.org/dotenv-expand/download/dotenv-expand-5.1.0.tgz", + "integrity": "sha1-P7rwIL/XlIhAcuomsel5HUWmKfA=", + "dev": true + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "http://registry.npm.taobao.org/duplexer3/download/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npm.taobao.org/duplexify/download/duplexify-3.7.1.tgz", + "integrity": "sha1-Kk31MX9sz9kfhtb9JdjYoQO4gwk=", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ejs": { + "version": "3.1.5", + "resolved": "https://registry.npm.taobao.org/ejs/download/ejs-3.1.5.tgz", + "integrity": "sha1-rtcjhE3CCstLFwzZqxAX5Hag2Ts=", + "dev": true, + "requires": { + "jake": "^10.6.1" + } + }, + "electron": { + "version": "10.1.3", + "resolved": "https://registry.npm.taobao.org/electron/download/electron-10.1.3.tgz", + "integrity": "sha1-fiduNzvzAHi9TLEYSFCpEmjcDmw=", + "dev": true, + "requires": { + "@electron/get": "^1.0.1", + "@types/node": "^12.0.12", + "extract-zip": "^1.0.3" + }, + "dependencies": { + "@types/node": { + "version": "12.19.5", + "resolved": "https://registry.npm.taobao.org/@types/node/download/@types/node-12.19.5.tgz?cache=0&sync_timestamp=1605654639177&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-12.19.5.tgz", + "integrity": "sha1-m+OUYTboGFl8ccYtBCQNBgLGRdQ=", + "dev": true + } + } + }, + "electron-builder": { + "version": "22.9.1", + "resolved": "https://registry.npm.taobao.org/electron-builder/download/electron-builder-22.9.1.tgz", + "integrity": "sha1-opYttvJ1e8AdAkifOPr+CAn2j2A=", + "dev": true, + "requires": { + "@types/yargs": "^15.0.5", + "app-builder-lib": "22.9.1", + "bluebird-lst": "^1.0.9", + "builder-util": "22.9.1", + "builder-util-runtime": "8.7.2", + "chalk": "^4.1.0", + "dmg-builder": "22.9.1", + "fs-extra": "^9.0.1", + "is-ci": "^2.0.0", + "lazy-val": "^1.0.4", + "read-config-file": "6.0.0", + "sanitize-filename": "^1.6.3", + "update-notifier": "^4.1.1", + "yargs": "^16.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npm.taobao.org/ansi-regex/download/ansi-regex-5.0.0.tgz", + "integrity": "sha1-OIU59VF5vzkznIGvMKZU1p+Hy3U=", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1601839226460&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz", + "integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-4.1.0.tgz", + "integrity": "sha1-ThSHCmGNni7dl92DRf2dncMVZGo=", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npm.taobao.org/cliui/download/cliui-7.0.4.tgz", + "integrity": "sha1-oCZe5lVHb8gHrqnfPfjfd4OAi08=", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz", + "integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz", + "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npm.taobao.org/emoji-regex/download/emoji-regex-8.0.0.tgz", + "integrity": "sha1-6Bj9ac5cz8tARZT4QpY79TFkzDc=", + "dev": true + }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npm.taobao.org/fs-extra/download/fs-extra-9.0.1.tgz", + "integrity": "sha1-kQ2gBiQ3ukw5/t2GPxZ1zP78ufw=", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-4.0.0.tgz", + "integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha1-8Rb4Bk/pCz94RKOJl8C3UFEmnx0=", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npm.taobao.org/jsonfile/download/jsonfile-6.1.0.tgz", + "integrity": "sha1-vFWyY0eTxnnsZAMJTrE2mKbsCq4=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/universalify/download/universalify-2.0.0.tgz", + "integrity": "sha1-daSYTv7cSwiXXFrrc/Uw0C3yVxc=", + "dev": true + } + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/string-width/download/string-width-4.2.0.tgz", + "integrity": "sha1-lSGCxGzHssMT0VluYjmSvRY7crU=", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-6.0.0.tgz?cache=0&sync_timestamp=1573280518303&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-6.0.0.tgz", + "integrity": "sha1-CxVx3XZpzNTz4G4U7x7tJiJa5TI=", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz?cache=0&sync_timestamp=1598611771865&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-7.2.0.tgz", + "integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/universalify/download/universalify-1.0.0.tgz", + "integrity": "sha1-thodoXPoQ1sv48Z9Kbmt+FlL0W0=", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npm.taobao.org/wrap-ansi/download/wrap-ansi-7.0.0.tgz", + "integrity": "sha1-Z+FFz/UQpqaYS98RUpEdadLrnkM=", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.5", + "resolved": "https://registry.npm.taobao.org/y18n/download/y18n-5.0.5.tgz?cache=0&sync_timestamp=1603637404399&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fy18n%2Fdownload%2Fy18n-5.0.5.tgz", + "integrity": "sha1-h2nsCNA7HqLfJQCs71YXQ7u5qxg=", + "dev": true + }, + "yargs": { + "version": "16.1.0", + "resolved": "https://registry.npm.taobao.org/yargs/download/yargs-16.1.0.tgz?cache=0&sync_timestamp=1602820925422&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fyargs%2Fdownload%2Fyargs-16.1.0.tgz", + "integrity": "sha1-/DM/5HkWYOrOWolLOdQvhRzUjyo=", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.2", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npm.taobao.org/yargs-parser/download/yargs-parser-20.2.4.tgz?cache=0&sync_timestamp=1604886709178&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fyargs-parser%2Fdownload%2Fyargs-parser-20.2.4.tgz", + "integrity": "sha1-tCiQ8UVmeW+Fro46JSkNIF8VSlQ=", + "dev": true + } + } + }, + "electron-is-accelerator": { + "version": "0.1.2", + "resolved": "https://registry.npm.taobao.org/electron-is-accelerator/download/electron-is-accelerator-0.1.2.tgz", + "integrity": "sha1-UJ5RDCala1Xhf4Y6SwThEYRqsns=", + "dev": true + }, + "electron-localshortcut": { + "version": "3.2.1", + "resolved": "https://registry.npm.taobao.org/electron-localshortcut/download/electron-localshortcut-3.2.1.tgz", + "integrity": "sha1-z8g6Pv9eKPr5jdzIf4Cizk9iPNM=", + "dev": true, + "requires": { + "debug": "^4.0.1", + "electron-is-accelerator": "^0.1.0", + "keyboardevent-from-electron-accelerator": "^2.0.0", + "keyboardevents-areequal": "^0.2.1" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-4.2.0.tgz?cache=0&sync_timestamp=1600502894812&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.2.0.tgz", + "integrity": "sha1-fxUPk5IOlMWPVXTC/QGjEQ7/5/E=", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + } + } + }, + "electron-notarize": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/electron-notarize/download/electron-notarize-1.0.0.tgz", + "integrity": "sha1-vJJbHMw/eeWOAp6MRwZXKwGp/Y8=", + "dev": true, + "requires": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-4.2.0.tgz?cache=0&sync_timestamp=1600502894812&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.2.0.tgz", + "integrity": "sha1-fxUPk5IOlMWPVXTC/QGjEQ7/5/E=", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npm.taobao.org/fs-extra/download/fs-extra-9.0.1.tgz?cache=0&sync_timestamp=1591229972229&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffs-extra%2Fdownload%2Ffs-extra-9.0.1.tgz", + "integrity": "sha1-kQ2gBiQ3ukw5/t2GPxZ1zP78ufw=", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npm.taobao.org/jsonfile/download/jsonfile-6.0.1.tgz", + "integrity": "sha1-mJZsuiFDeMjIS4LghZB7QL9hQXk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/universalify/download/universalify-1.0.0.tgz", + "integrity": "sha1-thodoXPoQ1sv48Z9Kbmt+FlL0W0=", + "dev": true + } + } + }, + "electron-osx-sign": { + "version": "0.4.17", + "resolved": "https://registry.npm.taobao.org/electron-osx-sign/download/electron-osx-sign-0.4.17.tgz", + "integrity": "sha1-JyfKDHnh5OXM04Yfs9qcPJE7AGw=", + "dev": true, + "requires": { + "bluebird": "^3.5.0", + "compare-version": "^0.1.2", + "debug": "^2.6.8", + "isbinaryfile": "^3.0.2", + "minimist": "^1.2.0", + "plist": "^3.0.1" + } + }, + "electron-packager": { + "version": "15.1.0", + "resolved": "https://registry.npm.taobao.org/electron-packager/download/electron-packager-15.1.0.tgz", + "integrity": "sha1-FqNzPkytJhEqKsNvCw81w7AXDv8=", + "dev": true, + "requires": { + "@electron/get": "^1.6.0", + "asar": "^3.0.0", + "debug": "^4.0.1", + "electron-notarize": "^1.0.0", + "electron-osx-sign": "^0.4.11", + "extract-zip": "^2.0.0", + "filenamify": "^4.1.0", + "fs-extra": "^9.0.0", + "galactus": "^0.2.1", + "get-package-info": "^1.0.0", + "junk": "^3.1.0", + "parse-author": "^2.0.0", + "plist": "^3.0.0", + "rcedit": "^2.0.0", + "resolve": "^1.1.6", + "semver": "^7.1.3", + "yargs-parser": "^19.0.1" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-4.2.0.tgz?cache=0&sync_timestamp=1600502894812&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.2.0.tgz", + "integrity": "sha1-fxUPk5IOlMWPVXTC/QGjEQ7/5/E=", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/extract-zip/download/extract-zip-2.0.1.tgz?cache=0&sync_timestamp=1591773082587&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fextract-zip%2Fdownload%2Fextract-zip-2.0.1.tgz", + "integrity": "sha1-Zj3KVv5G34kNXxMe9KBtIruLoTo=", + "dev": true, + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + } + }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npm.taobao.org/fs-extra/download/fs-extra-9.0.1.tgz?cache=0&sync_timestamp=1591229972229&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffs-extra%2Fdownload%2Ffs-extra-9.0.1.tgz", + "integrity": "sha1-kQ2gBiQ3ukw5/t2GPxZ1zP78ufw=", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npm.taobao.org/get-stream/download/get-stream-5.2.0.tgz?cache=0&sync_timestamp=1597056502934&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fget-stream%2Fdownload%2Fget-stream-5.2.0.tgz", + "integrity": "sha1-SWaheV7lrOZecGxLe+txJX1uItM=", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npm.taobao.org/jsonfile/download/jsonfile-6.0.1.tgz", + "integrity": "sha1-mJZsuiFDeMjIS4LghZB7QL9hQXk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npm.taobao.org/semver/download/semver-7.3.2.tgz", + "integrity": "sha1-YElisFK4HtB4aq6EOJ/7pw/9OTg=", + "dev": true + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/universalify/download/universalify-1.0.0.tgz", + "integrity": "sha1-thodoXPoQ1sv48Z9Kbmt+FlL0W0=", + "dev": true + }, + "yargs-parser": { + "version": "19.0.4", + "resolved": "https://registry.npm.taobao.org/yargs-parser/download/yargs-parser-19.0.4.tgz", + "integrity": "sha1-mRg6OlkmiyBcawQXfypb+0bnm6c=", + "dev": true + } + } + }, + "electron-publish": { + "version": "22.9.1", + "resolved": "https://registry.npm.taobao.org/electron-publish/download/electron-publish-22.9.1.tgz", + "integrity": "sha1-fMdqxMxT79Ke4xweX6y5ckMpBo4=", + "dev": true, + "requires": { + "@types/fs-extra": "^9.0.1", + "bluebird-lst": "^1.0.9", + "builder-util": "22.9.1", + "builder-util-runtime": "8.7.2", + "chalk": "^4.1.0", + "fs-extra": "^9.0.1", + "lazy-val": "^1.0.4", + "mime": "^2.4.6" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1601839226460&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz", + "integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-4.1.0.tgz", + "integrity": "sha1-ThSHCmGNni7dl92DRf2dncMVZGo=", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz", + "integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz", + "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=", + "dev": true + }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npm.taobao.org/fs-extra/download/fs-extra-9.0.1.tgz", + "integrity": "sha1-kQ2gBiQ3ukw5/t2GPxZ1zP78ufw=", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-4.0.0.tgz", + "integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npm.taobao.org/jsonfile/download/jsonfile-6.1.0.tgz", + "integrity": "sha1-vFWyY0eTxnnsZAMJTrE2mKbsCq4=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/universalify/download/universalify-2.0.0.tgz", + "integrity": "sha1-daSYTv7cSwiXXFrrc/Uw0C3yVxc=", + "dev": true + } + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz?cache=0&sync_timestamp=1598611771865&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-7.2.0.tgz", + "integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/universalify/download/universalify-1.0.0.tgz", + "integrity": "sha1-thodoXPoQ1sv48Z9Kbmt+FlL0W0=", + "dev": true + } + } + }, + "elliptic": { + "version": "6.5.3", + "resolved": "https://registry.npm.taobao.org/elliptic/download/elliptic-6.5.3.tgz", + "integrity": "sha1-y1nrLv2vc6C9eMzXAVpirW4Pk9Y=", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.11.9.tgz", + "integrity": "sha1-JtVWgpRY+dHoH8SJUkk9C6NQeCg=", + "dev": true + } + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npm.taobao.org/emoji-regex/download/emoji-regex-7.0.3.tgz", + "integrity": "sha1-kzoEBShgyF6DwSJHnEdIqOTHIVY=", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/emojis-list/download/emojis-list-3.0.0.tgz", + "integrity": "sha1-VXBmIEatKeLpFucariYKvf9Pang=", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/encodeurl/download/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true, + "optional": true + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npm.taobao.org/encoding/download/encoding-0.1.13.tgz", + "integrity": "sha1-VldK/deR9UqOmyeFwFgqLSYhD6k=", + "requires": { + "iconv-lite": "^0.6.2" + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npm.taobao.org/end-of-stream/download/end-of-stream-1.4.4.tgz", + "integrity": "sha1-WuZKX0UFe682JuwU2gyl5LJDHrA=", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.3.0", + "resolved": "https://registry.npm.taobao.org/enhanced-resolve/download/enhanced-resolve-4.3.0.tgz", + "integrity": "sha1-O4BvO/r8HsfeaVUe+TzKRsFwQSY=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npm.taobao.org/memory-fs/download/memory-fs-0.5.0.tgz", + "integrity": "sha1-MkwBKIuIZSlm0WHbd4OHIIRajjw=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + } + } + }, + "entities": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/entities/download/entities-2.1.0.tgz?cache=0&sync_timestamp=1602897029273&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fentities%2Fdownload%2Fentities-2.1.0.tgz", + "integrity": "sha1-mS0xKc999ocLlsV4WMJJoSD4uLU=" + }, + "env-paths": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/env-paths/download/env-paths-2.2.0.tgz", + "integrity": "sha1-zcpVfcAJFSkX1hZuL+vh8DloXkM=", + "dev": true + }, + "errno": { + "version": "0.1.7", + "resolved": "http://registry.npm.taobao.org/errno/download/errno-0.1.7.tgz", + "integrity": "sha1-RoTXF3mtOa8Xfj8AeZb3xnyFJhg=", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "http://registry.npm.taobao.org/error-ex/download/error-ex-1.3.2.tgz", + "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npm.taobao.org/es6-error/download/es6-error-4.1.1.tgz", + "integrity": "sha1-njr0B0Wd7tR+mpH5uIWoTrBcVh0=", + "dev": true, + "optional": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/escalade/download/escalade-3.1.1.tgz?cache=0&sync_timestamp=1602567230854&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fescalade%2Fdownload%2Fescalade-3.1.1.tgz", + "integrity": "sha1-2M/ccACWXFoBdLSoLqpcBVJ0LkA=", + "dev": true + }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/escape-goat/download/escape-goat-2.1.1.tgz", + "integrity": "sha1-Gy3HcANnbEV+x2Cy3GjttkgYhnU=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npm.taobao.org/eslint-scope/download/eslint-scope-4.0.3.tgz?cache=0&sync_timestamp=1599933651660&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-scope%2Fdownload%2Feslint-scope-4.0.3.tgz", + "integrity": "sha1-ygODMxD2iJoyZHgaqC5j65z+eEg=", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npm.taobao.org/esprima/download/esprima-4.0.1.tgz", + "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", + "dev": true + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npm.taobao.org/esrecurse/download/esrecurse-4.3.0.tgz?cache=0&sync_timestamp=1598898255610&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fesrecurse%2Fdownload%2Fesrecurse-4.3.0.tgz", + "integrity": "sha1-eteWTWeauyi+5yzsY3WLHF0smSE=", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npm.taobao.org/estraverse/download/estraverse-5.2.0.tgz?cache=0&sync_timestamp=1596642998635&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Festraverse%2Fdownload%2Festraverse-5.2.0.tgz", + "integrity": "sha1-MH30JUfmzHMk088DwVXVzbjFOIA=", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npm.taobao.org/estraverse/download/estraverse-4.3.0.tgz?cache=0&sync_timestamp=1596642998635&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Festraverse%2Fdownload%2Festraverse-4.3.0.tgz", + "integrity": "sha1-OYrT88WiSUi+dyXoPRGn3ijNvR0=", + "dev": true + }, + "events": { + "version": "3.2.0", + "resolved": "https://registry.npm.taobao.org/events/download/events-3.2.0.tgz?cache=0&sync_timestamp=1595422595227&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fevents%2Fdownload%2Fevents-3.2.0.tgz", + "integrity": "sha1-k7h8GPjvzUICpGGuxN/AVWtjk3k=", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "http://registry.npm.taobao.org/evp_bytestokey/download/evp_bytestokey-1.0.3.tgz", + "integrity": "sha1-f8vbGY3HGVlDLv4ThCaE4FJaywI=", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "http://registry.npm.taobao.org/expand-brackets/download/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "http://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "http://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "http://registry.npm.taobao.org/expand-tilde/download/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "http://registry.npm.taobao.org/extend/download/extend-3.0.2.tgz", + "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "http://registry.npm.taobao.org/extend-shallow/download/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/is-extendable/download/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "http://registry.npm.taobao.org/extglob/download/extglob-2.0.4.tgz", + "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/define-property/download/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "http://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/is-data-descriptor/download/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/is-descriptor/download/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extract-zip": { + "version": "1.7.0", + "resolved": "https://registry.npm.taobao.org/extract-zip/download/extract-zip-1.7.0.tgz?cache=0&sync_timestamp=1591773082587&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fextract-zip%2Fdownload%2Fextract-zip-1.7.0.tgz", + "integrity": "sha1-VWzDrp339FLEk6DPtRzDAneUCSc=", + "dev": true, + "requires": { + "concat-stream": "^1.6.2", + "debug": "^2.6.9", + "mkdirp": "^0.5.4", + "yauzl": "^2.10.0" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", + "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", + "dev": true + }, + "fast-glob": { + "version": "3.2.4", + "resolved": "https://registry.npm.taobao.org/fast-glob/download/fast-glob-3.2.4.tgz?cache=0&sync_timestamp=1592290365180&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffast-glob%2Fdownload%2Ffast-glob-3.2.4.tgz", + "integrity": "sha1-0grvv5lXk4Pn88xmUpFYybmFVNM=", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npm.taobao.org/braces/download/braces-3.0.2.tgz", + "integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npm.taobao.org/fill-range/download/fill-range-7.0.1.tgz", + "integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npm.taobao.org/is-number/download/is-number-7.0.0.tgz", + "integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npm.taobao.org/micromatch/download/micromatch-4.0.2.tgz", + "integrity": "sha1-T8sJmb+fvC/L3SEvbWKbmlbDklk=", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npm.taobao.org/to-regex-range/download/to-regex-range-5.0.1.tgz", + "integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/fast-json-stable-stringify/download/fast-json-stable-stringify-2.1.0.tgz?cache=0&sync_timestamp=1576367703577&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffast-json-stable-stringify%2Fdownload%2Ffast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM=", + "dev": true + }, + "fastq": { + "version": "1.9.0", + "resolved": "https://registry.npm.taobao.org/fastq/download/fastq-1.9.0.tgz?cache=0&sync_timestamp=1603877566746&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffastq%2Fdownload%2Ffastq-1.9.0.tgz", + "integrity": "sha1-4Wpy8zjqykjpG1wjWTvMLvZreUc=", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npm.taobao.org/fbjs/download/fbjs-0.8.17.tgz?cache=0&sync_timestamp=1602048886093&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffbjs%2Fdownload%2Ffbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npm.taobao.org/core-js/download/core-js-1.2.7.tgz?cache=0&sync_timestamp=1606329319777&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcore-js%2Fdownload%2Fcore-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npm.taobao.org/promise/download/promise-7.3.1.tgz", + "integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=", + "requires": { + "asap": "~2.0.3" + } + } + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "http://registry.npm.taobao.org/fd-slicer/download/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "fecha": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/fecha/download/fecha-4.2.0.tgz", + "integrity": "sha1-P/tjlUU+Pz7/+FBATwpZtnR/X0E=" + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npm.taobao.org/figgy-pudding/download/figgy-pudding-3.5.2.tgz", + "integrity": "sha1-tO7oFIq7Adzx0aw0Nn1Z4S+mHW4=", + "dev": true + }, + "file-loader": { + "version": "6.1.1", + "resolved": "https://registry.npm.taobao.org/file-loader/download/file-loader-6.1.1.tgz", + "integrity": "sha1-pvKd+z9ZM6HDULLbqiCsW+BTm6o=", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npm.taobao.org/json5/download/json5-2.1.3.tgz", + "integrity": "sha1-ybD3+pIzv+WAf+ZvzzpWF+1ZfUM=", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-2.0.0.tgz", + "integrity": "sha1-5MrOW4FtQloWa18JfhDNErNgZLA=", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-3.0.0.tgz", + "integrity": "sha1-Z1AvaqK2ai1AMrQnmilEl4oJE+8=", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/file-uri-to-path/download/file-uri-to-path-1.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffile-uri-to-path%2Fdownload%2Ffile-uri-to-path-1.0.0.tgz", + "integrity": "sha1-VTp7hEb/b2hDWcRF8eN6BdrMM90=", + "dev": true, + "optional": true + }, + "filelist": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/filelist/download/filelist-1.0.1.tgz", + "integrity": "sha1-8Q0aOuhsFpSAjo8gkG9D1MkTLbs=", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "filename-reserved-regex": { + "version": "2.0.0", + "resolved": "http://registry.npm.taobao.org/filename-reserved-regex/download/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=", + "dev": true + }, + "filenamify": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/filenamify/download/filenamify-4.2.0.tgz?cache=0&sync_timestamp=1600940508592&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffilenamify%2Fdownload%2Ffilenamify-4.2.0.tgz", + "integrity": "sha1-yZcW1naGlYWztdMos/BlkNAy6J8=", + "dev": true, + "requires": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "http://registry.npm.taobao.org/fill-range/download/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "http://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/find-cache-dir/download/find-cache-dir-2.1.0.tgz", + "integrity": "sha1-jQ+UzRP+Q8bHwmGg2GEVypGMBfc=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/find-up/download/find-up-3.0.0.tgz?cache=0&sync_timestamp=1597169842138&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffind-up%2Fdownload%2Ffind-up-3.0.0.tgz", + "integrity": "sha1-SRafHXmTQwZG2mHsxa41XCHJe3M=", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/findup-sync/download/findup-sync-3.0.0.tgz", + "integrity": "sha1-F7EI+e5RLft6XH88iyfqnhqcCNE=", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "flora-colossus": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/flora-colossus/download/flora-colossus-1.0.1.tgz", + "integrity": "sha1-q6GYQlqBhTQeZPnSpqlv2aPL25M=", + "dev": true, + "requires": { + "debug": "^4.1.1", + "fs-extra": "^7.0.0" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-4.2.0.tgz?cache=0&sync_timestamp=1600502894812&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.2.0.tgz", + "integrity": "sha1-fxUPk5IOlMWPVXTC/QGjEQ7/5/E=", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npm.taobao.org/fs-extra/download/fs-extra-7.0.1.tgz?cache=0&sync_timestamp=1591229972229&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffs-extra%2Fdownload%2Ffs-extra-7.0.1.tgz", + "integrity": "sha1-TxicRKoSO4lfcigE9V6iPq3DSOk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + } + } + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "http://registry.npm.taobao.org/flush-write-stream/download/flush-write-stream-1.1.1.tgz", + "integrity": "sha1-jdfYc6G6vCB9lOrQwuDkQnbr8ug=", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/for-in/download/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npm.taobao.org/form-data/download/form-data-2.5.1.tgz", + "integrity": "sha1-8svsV7XlniNxbhKP5E1OXdI4lfQ=", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "http://registry.npm.taobao.org/fragment-cache/download/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "from2": { + "version": "2.3.0", + "resolved": "http://registry.npm.taobao.org/from2/download/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/fs-minipass/download/fs-minipass-2.1.0.tgz", + "integrity": "sha1-f1A2/b8SxjwWkZDL5BmchSJx+fs=", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "http://registry.npm.taobao.org/fs-write-stream-atomic/download/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/fs.realpath/download/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-2.1.3.tgz", + "integrity": "sha1-+3OHA66NL5/pAMM4Nt3r7ouX8j4=", + "dev": true, + "optional": true + }, + "galactus": { + "version": "0.2.1", + "resolved": "https://registry.npm.taobao.org/galactus/download/galactus-0.2.1.tgz", + "integrity": "sha1-y+0tIKQMH1Z5o1kI4rlBVzPnjbk=", + "dev": true, + "requires": { + "debug": "^3.1.0", + "flora-colossus": "^1.0.0", + "fs-extra": "^4.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-3.2.6.tgz?cache=0&sync_timestamp=1600502894812&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-3.2.6.tgz", + "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npm.taobao.org/fs-extra/download/fs-extra-4.0.3.tgz?cache=0&sync_timestamp=1591229972229&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffs-extra%2Fdownload%2Ffs-extra-4.0.3.tgz", + "integrity": "sha1-DYUhIuW8W+tFP7Ao6cDJvzY0DJQ=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + } + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "http://registry.npm.taobao.org/get-caller-file/download/get-caller-file-2.0.5.tgz", + "integrity": "sha1-T5RBKoLbMvNuOwuXQfipf+sDH34=", + "dev": true + }, + "get-package-info": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/get-package-info/download/get-package-info-1.0.0.tgz", + "integrity": "sha1-ZDJ5ZWPigRPNlHTbvQAFKYWkmZw=", + "dev": true, + "requires": { + "bluebird": "^3.1.1", + "debug": "^2.2.0", + "lodash.get": "^4.0.0", + "read-pkg-up": "^2.0.0" + } + }, + "get-port": { + "version": "3.2.0", + "resolved": "https://registry.npm.taobao.org/get-port/download/get-port-3.2.0.tgz", + "integrity": "sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=" + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/get-stream/download/get-stream-4.1.0.tgz?cache=0&sync_timestamp=1597056502934&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fget-stream%2Fdownload%2Fget-stream-4.1.0.tgz", + "integrity": "sha1-wbJVV189wh1Zv8ec09K0axw6VLU=", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "http://registry.npm.taobao.org/get-value/download/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "gl-matrix": { + "version": "3.3.0", + "resolved": "https://registry.npm.taobao.org/gl-matrix/download/gl-matrix-3.3.0.tgz", + "integrity": "sha1-Iy7vYLHIswooy751ssr2xI/WNYs=" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npm.taobao.org/glob/download/glob-7.1.6.tgz", + "integrity": "sha1-FB8zuBp8JJLhJVlDB0gMRmeSeKY=", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npm.taobao.org/glob-parent/download/glob-parent-5.1.1.tgz", + "integrity": "sha1-tsHvQXxOVmPqSY8cRa+saRa7wik=", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "global-agent": { + "version": "2.1.12", + "resolved": "https://registry.npm.taobao.org/global-agent/download/global-agent-2.1.12.tgz", + "integrity": "sha1-5K44Ercxqegcv4Jfk3fvRQqOQZU=", + "dev": true, + "optional": true, + "requires": { + "boolean": "^3.0.1", + "core-js": "^3.6.5", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "dependencies": { + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npm.taobao.org/semver/download/semver-7.3.2.tgz", + "integrity": "sha1-YElisFK4HtB4aq6EOJ/7pw/9OTg=", + "dev": true, + "optional": true + } + } + }, + "global-dirs": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/global-dirs/download/global-dirs-2.0.1.tgz", + "integrity": "sha1-rN87tmhbzVXLNeigUiZlaelGkgE=", + "dev": true, + "requires": { + "ini": "^1.3.5" + } + }, + "global-modules": { + "version": "2.0.0", + "resolved": "http://registry.npm.taobao.org/global-modules/download/global-modules-2.0.0.tgz", + "integrity": "sha1-mXYFrSNF8n9RU5vqJldEISFcd4A=", + "dev": true, + "requires": { + "global-prefix": "^3.0.0" + }, + "dependencies": { + "global-prefix": { + "version": "3.0.0", + "resolved": "http://registry.npm.taobao.org/global-prefix/download/global-prefix-3.0.0.tgz", + "integrity": "sha1-/IX3MGTfafUEIfR/iD/luRO6m5c=", + "dev": true, + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + } + } + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/global-prefix/download/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "global-tunnel-ng": { + "version": "2.7.1", + "resolved": "https://registry.npm.taobao.org/global-tunnel-ng/download/global-tunnel-ng-2.7.1.tgz", + "integrity": "sha1-0DtRAt/eOmmRT17n2GdhyjXVfY8=", + "dev": true, + "optional": true, + "requires": { + "encodeurl": "^1.0.2", + "lodash": "^4.17.10", + "npm-conf": "^1.1.3", + "tunnel": "^0.0.6" + } + }, + "globalthis": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/globalthis/download/globalthis-1.0.1.tgz", + "integrity": "sha1-QBFvXZwHH56PsAN2VN8as6g7fvk=", + "dev": true, + "optional": true, + "requires": { + "define-properties": "^1.1.3" + } + }, + "globby": { + "version": "11.0.1", + "resolved": "https://registry.npm.taobao.org/globby/download/globby-11.0.1.tgz?cache=0&sync_timestamp=1600349143804&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fglobby%2Fdownload%2Fglobby-11.0.1.tgz", + "integrity": "sha1-mivxB6Bo8//qvEmtcCx57ejP01c=", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npm.taobao.org/got/download/got-9.6.0.tgz?cache=0&sync_timestamp=1600463162943&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fgot%2Fdownload%2Fgot-9.6.0.tgz", + "integrity": "sha1-7fRefWf5lUVwXeH3u+7rEhdl7YU=", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.2.4.tgz", + "integrity": "sha1-Ila94U02MpWMRl68ltxGfKB6Kfs=", + "dev": true + }, + "graphlib": { + "version": "2.1.8", + "resolved": "https://registry.npm.taobao.org/graphlib/download/graphlib-2.1.8.tgz", + "integrity": "sha1-V2HUFHN4cAhMkux7XbywWSydNdo=", + "requires": { + "lodash": "^4.17.15" + } + }, + "gud": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/gud/download/gud-1.0.0.tgz", + "integrity": "sha1-pIlYGxfmpwvsqavjrlfeekmYUsA=" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "http://registry.npm.taobao.org/has-flag/download/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/has-value/download/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/has-values/download/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/has-yarn/download/has-yarn-2.1.0.tgz", + "integrity": "sha1-E34RNUp7W/EapctknPDG8/8rLnc=", + "dev": true + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/hash-base/download/hash-base-3.1.0.tgz", + "integrity": "sha1-VcOB2eBuHSmXqIO0o/3f5/DTrzM=", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npm.taobao.org/readable-stream/download/readable-stream-3.6.0.tgz?cache=0&sync_timestamp=1581624324274&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freadable-stream%2Fdownload%2Freadable-stream-3.6.0.tgz", + "integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.2.1.tgz", + "integrity": "sha1-Hq+fqb2x/dTsdfWPnNtOa3gn7sY=", + "dev": true + } + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "http://registry.npm.taobao.org/hash.js/download/hash.js-1.1.7.tgz", + "integrity": "sha1-C6vKU46NTuSg+JiNaIZlN6ADz0I=", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/hmac-drbg/download/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npm.taobao.org/hoist-non-react-statics/download/hoist-non-react-statics-3.3.2.tgz?cache=0&sync_timestamp=1589806086077&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhoist-non-react-statics%2Fdownload%2Fhoist-non-react-statics-3.3.2.tgz", + "integrity": "sha1-7OCsr3HWLClpwuxZ/v9CpLGoW0U=", + "requires": { + "react-is": "^16.7.0" + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "http://registry.npm.taobao.org/homedir-polyfill/download/homedir-polyfill-1.0.3.tgz", + "integrity": "sha1-dDKYzvTlrz4ZQWH7rcwhUdOgWOg=", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.8.8.tgz?cache=0&sync_timestamp=1594427993800&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhosted-git-info%2Fdownload%2Fhosted-git-info-2.8.8.tgz", + "integrity": "sha1-dTm9S8Hg4KiVgVouAmJCCxKFhIg=", + "dev": true + }, + "html-to-react": { + "version": "1.4.5", + "resolved": "https://registry.npm.taobao.org/html-to-react/download/html-to-react-1.4.5.tgz", + "integrity": "sha1-WQkcEQIdHvMV73OEYKu2pKQf4c4=", + "requires": { + "domhandler": "^3.3.0", + "htmlparser2": "^5.0", + "lodash.camelcase": "^4.3.0", + "ramda": "^0.27.1" + } + }, + "htmlparser2": { + "version": "5.0.1", + "resolved": "https://registry.npm.taobao.org/htmlparser2/download/htmlparser2-5.0.1.tgz?cache=0&sync_timestamp=1603668161350&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtmlparser2%2Fdownload%2Fhtmlparser2-5.0.1.tgz", + "integrity": "sha1-fapvw+NdYQeslaT8CHgfCRZk9uc=", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^3.3.0", + "domutils": "^2.4.2", + "entities": "^2.0.0" + } + }, + "http-basic": { + "version": "8.1.3", + "resolved": "https://registry.npm.taobao.org/http-basic/download/http-basic-8.1.3.tgz", + "integrity": "sha1-p8q+51Joabm3EBNpcIBbEAQmG78=", + "requires": { + "caseless": "^0.12.0", + "concat-stream": "^1.6.2", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/http-cache-semantics/download/http-cache-semantics-4.1.0.tgz", + "integrity": "sha1-SekcXL82yblLz81xwj1SSex045A=", + "dev": true + }, + "http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npm.taobao.org/http-response-object/download/http-response-object-3.0.2.tgz", + "integrity": "sha1-f0NbshBFTkNg0HTvH5idXqiqmBA=", + "requires": { + "@types/node": "^10.0.3" + }, + "dependencies": { + "@types/node": { + "version": "10.17.46", + "resolved": "https://registry.npm.taobao.org/@types/node/download/@types/node-10.17.46.tgz?cache=0&sync_timestamp=1605819795096&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-10.17.46.tgz", + "integrity": "sha1-HNhn6/6ZV6tFlR8vcV+N5fPat6M=" + } + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/https-browserify/download/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "iconv-lite": { + "version": "0.6.2", + "resolved": "https://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.6.2.tgz", + "integrity": "sha1-zhPRh1sMOmdL1qBLf3awGxtt7QE=", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npm.taobao.org/icss-utils/download/icss-utils-4.1.1.tgz?cache=0&sync_timestamp=1600767346060&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ficss-utils%2Fdownload%2Ficss-utils-4.1.1.tgz", + "integrity": "sha1-IRcLU3ie4nRHwvR91oMIFAP5pGc=", + "dev": true, + "requires": { + "postcss": "^7.0.14" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npm.taobao.org/ieee754/download/ieee754-1.1.13.tgz", + "integrity": "sha1-7BaFWOlaoYH9h9N/VcMrvLZwi4Q=", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "http://registry.npm.taobao.org/iferr/download/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npm.taobao.org/ignore/download/ignore-5.1.8.tgz", + "integrity": "sha1-8VCotQo0KJsz4i9YiavU2AFvDlc=", + "dev": true + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npm.taobao.org/image-size/download/image-size-0.5.5.tgz?cache=0&sync_timestamp=1603731285656&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fimage-size%2Fdownload%2Fimage-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true + }, + "immer": { + "version": "4.0.2", + "resolved": "https://registry.npm.taobao.org/immer/download/immer-4.0.2.tgz", + "integrity": "sha1-n/D834jgb5Jhill4zuy1iE5jNVk=" + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/import-lazy/download/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/import-local/download/import-local-2.0.0.tgz", + "integrity": "sha1-VQcL44pZk88Y72236WH1vuXFoJ0=", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "http://registry.npm.taobao.org/imurmurhash/download/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/indent-string/download/indent-string-4.0.0.tgz", + "integrity": "sha1-Yk+PRJfWGbLZdoUx1Y9BIoVNclE=", + "dev": true + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/indexes-of/download/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/infer-owner/download/infer-owner-1.0.4.tgz", + "integrity": "sha1-xM78qo5RBRwqQLos6KPScpWvlGc=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "http://registry.npm.taobao.org/inflight/download/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.4.tgz?cache=0&sync_timestamp=1560975547815&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Finherits%2Fdownload%2Finherits-2.0.4.tgz", + "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=" + }, + "ini": { + "version": "1.3.5", + "resolved": "http://registry.npm.taobao.org/ini/download/ini-1.3.5.tgz", + "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=", + "dev": true + }, + "insert-css": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/insert-css/download/insert-css-2.0.0.tgz", + "integrity": "sha1-610Ql7dUL0x56jBg067gfQU4gPQ=" + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npm.taobao.org/interpret/download/interpret-1.4.0.tgz", + "integrity": "sha1-Zlq4vE2iendKQFhOgS4+D6RbGh4=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "http://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/is-alphabetical/download/is-alphabetical-1.0.4.tgz", + "integrity": "sha1-nn1rlJFr4iFTdF0YTCmMv5hqaG0=" + }, + "is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/is-alphanumerical/download/is-alphanumerical-1.0.4.tgz", + "integrity": "sha1-frmiQx+FX2se8aeOMm31FWlsTb8=", + "requires": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, + "is-any-array": { + "version": "0.1.0", + "resolved": "https://registry.npm.taobao.org/is-any-array/download/is-any-array-0.1.0.tgz", + "integrity": "sha1-5aJ5ZeM3FhDBDGa8o7437hd7344=", + "requires": { + "rollup": "^1.31.1" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "http://registry.npm.taobao.org/is-arrayish/download/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "http://registry.npm.taobao.org/is-binary-path/download/is-binary-path-2.1.0.tgz", + "integrity": "sha1-6h9/O4DwZCNug0cPhsCcJU+0Wwk=", + "dev": true, + "optional": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npm.taobao.org/is-buffer/download/is-buffer-1.1.6.tgz", + "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/is-ci/download/is-ci-2.0.0.tgz", + "integrity": "sha1-a8YzQYGBDgS1wis9WJ/cpVAmQEw=", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "http://registry.npm.taobao.org/is-data-descriptor/download/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/is-decimal/download/is-decimal-1.0.4.tgz", + "integrity": "sha1-ZaOllYocW2OnBuGzM9fNn2MNP6U=" + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "http://registry.npm.taobao.org/is-descriptor/download/is-descriptor-0.1.6.tgz", + "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-5.1.0.tgz", + "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "http://registry.npm.taobao.org/is-extendable/download/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "http://registry.npm.taobao.org/is-extglob/download/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "http://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "http://registry.npm.taobao.org/is-glob/download/is-glob-4.0.1.tgz", + "integrity": "sha1-dWfb6fL14kZ7x3q4PEopSCQHpdw=", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/is-hexadecimal/download/is-hexadecimal-1.0.4.tgz", + "integrity": "sha1-zDXJdYjaS9Saju3WvECC1E3LI6c=" + }, + "is-installed-globally": { + "version": "0.3.2", + "resolved": "https://registry.npm.taobao.org/is-installed-globally/download/is-installed-globally-0.3.2.tgz", + "integrity": "sha1-/T76ee5nDRGHIzGC1bCh3QAxMUE=", + "dev": true, + "requires": { + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" + } + }, + "is-npm": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/is-npm/download/is-npm-4.0.0.tgz", + "integrity": "sha1-yQ3YOAaW34enptgjwg0LErvjyE0=", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "http://registry.npm.taobao.org/is-number/download/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/is-obj/download/is-obj-2.0.0.tgz", + "integrity": "sha1-Rz+wXZc3BeP9liBUUBjKjiLvSYI=", + "dev": true + }, + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npm.taobao.org/is-path-inside/download/is-path-inside-3.0.2.tgz", + "integrity": "sha1-9SIPyCo+IzdXKR3dycWHfyofMBc=", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/is-plain-obj/download/is-plain-obj-2.1.0.tgz?cache=0&sync_timestamp=1602541451286&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-plain-obj%2Fdownload%2Fis-plain-obj-2.1.0.tgz", + "integrity": "sha1-ReQuN/zPH0Dajl927iFRWEDAkoc=" + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npm.taobao.org/is-plain-object/download/is-plain-object-2.0.4.tgz", + "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/is-stream/download/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-typedarray/download/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/is-windows/download/is-windows-1.0.2.tgz", + "integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/is-wsl/download/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npm.taobao.org/is-yarn-global/download/is-yarn-global-0.3.0.tgz", + "integrity": "sha1-1QLTOCWQ6jAEiTdGdUyJE5lz4jI=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/isarray/download/isarray-1.0.0.tgz?cache=0&sync_timestamp=1562592096220&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fisarray%2Fdownload%2Fisarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isbinaryfile": { + "version": "3.0.3", + "resolved": "https://registry.npm.taobao.org/isbinaryfile/download/isbinaryfile-3.0.3.tgz", + "integrity": "sha1-XW3vPt6/boyoyunDAYOoBLX4voA=", + "dev": true, + "requires": { + "buffer-alloc": "^1.2.0" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "http://registry.npm.taobao.org/isexe/download/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npm.taobao.org/isobject/download/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/isomorphic-fetch/download/isomorphic-fetch-2.2.1.tgz?cache=0&sync_timestamp=1600844260369&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fisomorphic-fetch%2Fdownload%2Fisomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + }, + "dependencies": { + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npm.taobao.org/node-fetch/download/node-fetch-1.7.3.tgz?cache=0&sync_timestamp=1599309179354&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnode-fetch%2Fdownload%2Fnode-fetch-1.7.3.tgz", + "integrity": "sha1-mA9vcthSEaU0fGsrwYxbhMPrR+8=", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + } + } + }, + "jake": { + "version": "10.8.2", + "resolved": "https://registry.npm.taobao.org/jake/download/jake-10.8.2.tgz", + "integrity": "sha1-68nehVgWCmbYLQ6txqLlj7xQCns=", + "dev": true, + "requires": { + "async": "0.9.x", + "chalk": "^2.4.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/js-tokens/download/js-tokens-4.0.0.tgz", + "integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk=" + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npm.taobao.org/js-yaml/download/js-yaml-3.14.0.tgz", + "integrity": "sha1-p6NBcPJqIbsWJCTYray0ETpp5II=", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "http://registry.npm.taobao.org/json-buffer/download/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/json-parse-better-errors/download/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha1-u4Z8+zRQ5pEHwTHRxRS6s9yLyqk=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599333856086&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "http://registry.npm.taobao.org/json-stringify-safe/download/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true, + "optional": true + }, + "json2mq": { + "version": "0.2.0", + "resolved": "http://registry.npm.taobao.org/json2mq/download/json2mq-0.2.0.tgz", + "integrity": "sha1-tje9O6nqvhIsg+lyBIOusQ0skEo=", + "requires": { + "string-convert": "^0.2.0" + } + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/json5/download/json5-1.0.1.tgz", + "integrity": "sha1-d5+wAYYE+oVOrL9iUhgNg1Q+Pb4=", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/jsonfile/download/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "junk": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/junk/download/junk-3.1.0.tgz", + "integrity": "sha1-MUmQmNkCt+mMXZucgPQ0V6iKv6E=", + "dev": true + }, + "keyboardevent-from-electron-accelerator": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/keyboardevent-from-electron-accelerator/download/keyboardevent-from-electron-accelerator-2.0.0.tgz", + "integrity": "sha1-rOIbGqTkcUiBXRYAV/nttmVnxQw=", + "dev": true + }, + "keyboardevents-areequal": { + "version": "0.2.2", + "resolved": "https://registry.npm.taobao.org/keyboardevents-areequal/download/keyboardevents-areequal-0.2.2.tgz", + "integrity": "sha1-iBkexzjOn3WRwl6QVt6Si0AncZQ=", + "dev": true + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/keyv/download/keyv-3.1.0.tgz?cache=0&sync_timestamp=1600337463601&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fkeyv%2Fdownload%2Fkeyv-3.1.0.tgz", + "integrity": "sha1-7MIoSG9pmR5J6UdkhaW+Ho/FxNk=", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true + }, + "klona": { + "version": "2.0.4", + "resolved": "https://registry.npm.taobao.org/klona/download/klona-2.0.4.tgz", + "integrity": "sha1-e7Hjr/sMuGJFR+9+j2cI6i4538A=", + "dev": true + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npm.taobao.org/latest-version/download/latest-version-5.1.0.tgz", + "integrity": "sha1-EZ3+kI/jjRXfpD7NE/oS7Igy+s4=", + "dev": true, + "requires": { + "package-json": "^6.3.0" + } + }, + "lazy-val": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/lazy-val/download/lazy-val-1.0.4.tgz", + "integrity": "sha1-iCY2pyRcLP5uCk47psXWihN+XGU=", + "dev": true + }, + "less": { + "version": "3.12.2", + "resolved": "https://registry.npm.taobao.org/less/download/less-3.12.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fless%2Fdownload%2Fless-3.12.2.tgz", + "integrity": "sha1-FX5t0ypohp34hZMUrTjnAhGvOrQ=", + "dev": true, + "requires": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "native-request": "^1.0.5", + "source-map": "~0.6.0", + "tslib": "^1.10.0" + }, + "dependencies": { + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz", + "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=", + "dev": true, + "optional": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "dev": true, + "optional": true + } + } + }, + "less-loader": { + "version": "7.1.0", + "resolved": "https://registry.npm.taobao.org/less-loader/download/less-loader-7.1.0.tgz?cache=0&sync_timestamp=1605095927771&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fless-loader%2Fdownload%2Fless-loader-7.1.0.tgz", + "integrity": "sha1-lY1B6G194Ly0kHEe4PI1qp3Flqo=", + "dev": true, + "requires": { + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npm.taobao.org/json5/download/json5-2.1.3.tgz", + "integrity": "sha1-ybD3+pIzv+WAf+ZvzzpWF+1ZfUM=", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-2.0.0.tgz", + "integrity": "sha1-5MrOW4FtQloWa18JfhDNErNgZLA=", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-3.0.0.tgz", + "integrity": "sha1-Z1AvaqK2ai1AMrQnmilEl4oJE+8=", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/load-json-file/download/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npm.taobao.org/pify/download/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npm.taobao.org/loader-runner/download/loader-runner-2.4.0.tgz?cache=0&sync_timestamp=1601450715716&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Floader-runner%2Fdownload%2Floader-runner-2.4.0.tgz", + "integrity": "sha1-7UcGa/5TTX6ExMe5mYwqdWB9k1c=", + "dev": true + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-1.4.0.tgz", + "integrity": "sha1-xXm140yzSxp07cbB+za/o3HVphM=", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/locate-path/download/locate-path-3.0.0.tgz", + "integrity": "sha1-2+w7OrdZdYBxtY/ln8QYca8hQA4=", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npm.taobao.org/lodash/download/lodash-4.17.20.tgz?cache=0&sync_timestamp=1597335994883&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash%2Fdownload%2Flodash-4.17.20.tgz", + "integrity": "sha1-tEqbYpe8tpjxxRo1RaKzs2jVnFI=" + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "http://registry.npm.taobao.org/lodash.camelcase/download/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "http://registry.npm.taobao.org/lodash.get/download/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "http://registry.npm.taobao.org/loose-envify/download/loose-envify-1.4.0.tgz", + "integrity": "sha1-ce5R+nvkyuwaY4OffmgtgTLTDK8=", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/lowercase-keys/download/lowercase-keys-1.0.1.tgz", + "integrity": "sha1-b54wtHCE2XGnyCD/FabFFnt0wm8=", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npm.taobao.org/lru-cache/download/lru-cache-5.1.1.tgz?cache=0&sync_timestamp=1594427582110&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flru-cache%2Fdownload%2Flru-cache-5.1.1.tgz", + "integrity": "sha1-HaJ+ZxAnGUdpXa9oSOhH8B2EuSA=", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/make-dir/download/make-dir-2.1.0.tgz", + "integrity": "sha1-XwMQ4YuL6JjMBwCSlaMK5B6R5vU=", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "http://registry.npm.taobao.org/map-cache/download/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/map-visit/download/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "matcher": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/matcher/download/matcher-3.0.0.tgz", + "integrity": "sha1-vZBg9MW3CqgEHMxvgDaHYJlPMMo=", + "dev": true, + "optional": true, + "requires": { + "escape-string-regexp": "^4.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-4.0.0.tgz", + "integrity": "sha1-FLqDpdNz49MR5a/KKc9b+tllvzQ=", + "dev": true, + "optional": true + } + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "http://registry.npm.taobao.org/md5.js/download/md5.js-1.3.5.tgz", + "integrity": "sha1-tdB7jjIW4+J81yjXL3DR5qNCAF8=", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mdast-add-list-metadata": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/mdast-add-list-metadata/download/mdast-add-list-metadata-1.0.1.tgz", + "integrity": "sha1-lec2QM4vwfoty37EQ9CeK/59tM8=", + "requires": { + "unist-util-visit-parents": "1.1.2" + } + }, + "mdast-util-from-markdown": { + "version": "0.8.1", + "resolved": "https://registry.npm.taobao.org/mdast-util-from-markdown/download/mdast-util-from-markdown-0.8.1.tgz", + "integrity": "sha1-eBNx1JPKwRISlHImGQJwwV3JcRY=", + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-to-string": "^1.0.0", + "micromark": "~2.10.0", + "parse-entities": "^2.0.0" + } + }, + "mdast-util-to-string": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/mdast-util-to-string/download/mdast-util-to-string-1.1.0.tgz", + "integrity": "sha1-JwVVABA/UWN70H0B2gHrGWekNSc=" + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npm.taobao.org/memory-fs/download/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npm.taobao.org/merge2/download/merge2-1.4.1.tgz", + "integrity": "sha1-Q2iJL4hekHRVpv19xVwMnUBJkK4=", + "dev": true + }, + "micromark": { + "version": "2.10.1", + "resolved": "https://registry.npm.taobao.org/micromark/download/micromark-2.10.1.tgz", + "integrity": "sha1-zXP1TgZW8Q5jMHPbJrZjoiGkQqc=", + "requires": { + "debug": "^4.0.0", + "parse-entities": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-4.2.0.tgz?cache=0&sync_timestamp=1600502894812&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.2.0.tgz", + "integrity": "sha1-fxUPk5IOlMWPVXTC/QGjEQ7/5/E=", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=" + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npm.taobao.org/micromatch/download/micromatch-3.1.10.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmicromatch%2Fdownload%2Fmicromatch-3.1.10.tgz", + "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "http://registry.npm.taobao.org/miller-rabin/download/miller-rabin-4.0.1.tgz", + "integrity": "sha1-8IA1HIZbDcViqEYpZtqlNUPHik0=", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.11.9.tgz", + "integrity": "sha1-JtVWgpRY+dHoH8SJUkk9C6NQeCg=", + "dev": true + } + } + }, + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npm.taobao.org/mime/download/mime-2.4.6.tgz", + "integrity": "sha1-5bQHyQ20QvK+tbFiNz0Htpr/pNE=", + "dev": true + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npm.taobao.org/mime-db/download/mime-db-1.44.0.tgz?cache=0&sync_timestamp=1600831175828&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime-db%2Fdownload%2Fmime-db-1.44.0.tgz", + "integrity": "sha1-+hHF6wrKEzS0Izy01S8QxaYnL5I=" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npm.taobao.org/mime-types/download/mime-types-2.1.27.tgz", + "integrity": "sha1-R5SfmOJ56lMRn1ci4PNOUpvsAJ8=", + "requires": { + "mime-db": "1.44.0" + } + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/mimic-response/download/mimic-response-1.0.1.tgz", + "integrity": "sha1-SSNTiHju9CBjy4o+OweYeBSHqxs=", + "dev": true + }, + "mini-store": { + "version": "3.0.6", + "resolved": "https://registry.npm.taobao.org/mini-store/download/mini-store-3.0.6.tgz?cache=0&sync_timestamp=1596178843083&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmini-store%2Fdownload%2Fmini-store-3.0.6.tgz", + "integrity": "sha1-RLhr5bKHcnEiTOBomzo1ot/7HKk=", + "requires": { + "hoist-non-react-statics": "^3.3.2", + "shallowequal": "^1.0.2" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/minimalistic-assert/download/minimalistic-assert-1.0.1.tgz", + "integrity": "sha1-LhlN4ERibUoQ5/f7wAznPoPk1cc=", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/minimalistic-crypto-utils/download/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "http://registry.npm.taobao.org/minimatch/download/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npm.taobao.org/minimist/download/minimist-1.2.5.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fminimist%2Fdownload%2Fminimist-1.2.5.tgz", + "integrity": "sha1-Z9ZgFLZqaoqqDAg8X9WN9OTpdgI=", + "dev": true + }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npm.taobao.org/minipass/download/minipass-3.1.3.tgz?cache=0&sync_timestamp=1600349143244&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fminipass%2Fdownload%2Fminipass-3.1.3.tgz", + "integrity": "sha1-fUL/HzljVILhX5zbUxhN7r1YFf0=", + "dev": true, + "requires": { + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/yallist/download/yallist-4.0.0.tgz", + "integrity": "sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=", + "dev": true + } + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/minipass-collect/download/minipass-collect-1.0.2.tgz", + "integrity": "sha1-IrgTv3Rdxu26JXa5QAIq1u3Ixhc=", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npm.taobao.org/minipass-flush/download/minipass-flush-1.0.5.tgz", + "integrity": "sha1-gucTXX6JpQ/+ZGEKeHlTxMTLs3M=", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npm.taobao.org/minipass-pipeline/download/minipass-pipeline-1.2.4.tgz", + "integrity": "sha1-aEcveXEcCEZXwGfFxq2Tzd6oIUw=", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/minizlib/download/minizlib-2.1.2.tgz", + "integrity": "sha1-6Q00Zrogm5MkUVCKEc49NjIUWTE=", + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/yallist/download/yallist-4.0.0.tgz", + "integrity": "sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=", + "dev": true + } + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/mississippi/download/mississippi-3.0.0.tgz", + "integrity": "sha1-6goykfl+C16HdrNj1fChLZTGcCI=", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npm.taobao.org/mixin-deep/download/mixin-deep-1.3.2.tgz?cache=0&sync_timestamp=1561436244196&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmixin-deep%2Fdownload%2Fmixin-deep-1.3.2.tgz", + "integrity": "sha1-ESC0PcNZp4Xc5ltVuC4lfM9HlWY=", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/is-extendable/download/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.5.tgz?cache=0&sync_timestamp=1587535418745&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmkdirp%2Fdownload%2Fmkdirp-0.5.5.tgz", + "integrity": "sha1-2Rzv1i0UNsoPQWIOJRKI1CAJne8=", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ml-array-max": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/ml-array-max/download/ml-array-max-1.2.0.tgz", + "integrity": "sha1-FBWVEx/hAgjdiYl86Yq3/TgqOVE=", + "requires": { + "is-any-array": "^0.1.0" + } + }, + "ml-array-min": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/ml-array-min/download/ml-array-min-1.2.0.tgz", + "integrity": "sha1-aIDqMZJQqZ7HO8J5msAFwQoPBIk=", + "requires": { + "is-any-array": "^0.1.0" + } + }, + "ml-array-rescale": { + "version": "1.3.2", + "resolved": "https://registry.npm.taobao.org/ml-array-rescale/download/ml-array-rescale-1.3.2.tgz", + "integrity": "sha1-696qqE0V9xTbsA6UpqkzbdzxBBM=", + "requires": { + "is-any-array": "^0.1.0", + "ml-array-max": "^1.2.0", + "ml-array-min": "^1.2.0" + } + }, + "ml-matrix": { + "version": "6.5.3", + "resolved": "https://registry.npm.taobao.org/ml-matrix/download/ml-matrix-6.5.3.tgz", + "integrity": "sha1-DEuyZxRgfKx20om5QxcccINBju0=", + "requires": { + "ml-array-rescale": "^1.3.2" + } + }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npm.taobao.org/moment/download/moment-2.29.1.tgz", + "integrity": "sha1-sr52n6MZQL6e7qZGnAdeNQBvo9M=" + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npm.taobao.org/rimraf/download/rimraf-2.7.1.tgz?cache=0&sync_timestamp=1581257110269&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frimraf%2Fdownload%2Frimraf-2.7.1.tgz", + "integrity": "sha1-NXl/E6f9rcVmFCwp1PB8ytSD4+w=", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "nan": { + "version": "2.14.1", + "resolved": "https://registry.npm.taobao.org/nan/download/nan-2.14.1.tgz?cache=0&sync_timestamp=1589682780413&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnan%2Fdownload%2Fnan-2.14.1.tgz", + "integrity": "sha1-174036MQW5FJTDFHCJMV7/iHSwE=", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "http://registry.npm.taobao.org/nanomatch/download/nanomatch-1.2.13.tgz", + "integrity": "sha1-uHqKpPwN6P5r6IiVs4mD/yZb0Rk=", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "native-request": { + "version": "1.0.8", + "resolved": "https://registry.npm.taobao.org/native-request/download/native-request-1.0.8.tgz?cache=0&sync_timestamp=1603412804149&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnative-request%2Fdownload%2Fnative-request-1.0.8.tgz", + "integrity": "sha1-j2a/YG4PfqJ8DlmV6y9dA+M65vs=", + "dev": true, + "optional": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npm.taobao.org/neo-async/download/neo-async-2.6.2.tgz", + "integrity": "sha1-tKr7k+OustgXTKU88WOrfXMIMF8=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npm.taobao.org/nice-try/download/nice-try-1.0.5.tgz", + "integrity": "sha1-ozeKdpbOfSI+iPybdkvX7xCJ42Y=", + "dev": true + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npm.taobao.org/node-fetch/download/node-fetch-2.6.1.tgz?cache=0&sync_timestamp=1599309179354&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnode-fetch%2Fdownload%2Fnode-fetch-2.6.1.tgz", + "integrity": "sha1-BFvTI2Mfdu0uK1VXM5RBa2OaAFI=", + "dev": true + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/node-libs-browser/download/node-libs-browser-2.2.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnode-libs-browser%2Fdownload%2Fnode-libs-browser-2.2.1.tgz", + "integrity": "sha1-tk9RPRgzhiX5A0bSew0jXmMfZCU=", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "http://registry.npm.taobao.org/punycode/download/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "http://registry.npm.taobao.org/normalize-package-data/download/normalize-package-data-2.5.0.tgz", + "integrity": "sha1-5m2xg4sgDB38IzIl0SyzZSDiNKg=", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "http://registry.npm.taobao.org/normalize-path/download/normalize-path-3.0.0.tgz", + "integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=", + "dev": true + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npm.taobao.org/normalize-url/download/normalize-url-4.5.0.tgz", + "integrity": "sha1-RTNUCH5sqWlXvY9br3U/WYIUISk=", + "dev": true + }, + "npm-conf": { + "version": "1.1.3", + "resolved": "http://registry.npm.taobao.org/npm-conf/download/npm-conf-1.1.3.tgz", + "integrity": "sha1-JWzEe9DiGMJZxOlVC/QTvCGSr/k=", + "dev": true, + "optional": true, + "requires": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/pify/download/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "optional": true + } + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "http://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "http://registry.npm.taobao.org/object-copy/download/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "http://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "http://registry.npm.taobao.org/object-keys/download/object-keys-1.1.1.tgz", + "integrity": "sha1-HEfyct8nfzsdrwYWd9nILiMixg4=", + "dev": true, + "optional": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/object-visit/download/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "http://registry.npm.taobao.org/object.pick/download/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "omit.js": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/omit.js/download/omit.js-2.0.2.tgz", + "integrity": "sha1-3ZuENvq5R6Xz/yFMslOGMeMT7C8=" + }, + "once": { + "version": "1.4.0", + "resolved": "http://registry.npm.taobao.org/once/download/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "http://registry.npm.taobao.org/os-browserify/download/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/p-cancelable/download/p-cancelable-1.1.0.tgz", + "integrity": "sha1-0HjRWjr0CSIMiG8dmgyi5EGrJsw=", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npm.taobao.org/p-limit/download/p-limit-2.3.0.tgz", + "integrity": "sha1-PdM8ZHohT9//2DWTPrCG2g3CHbE=", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/p-locate/download/p-locate-3.0.0.tgz", + "integrity": "sha1-Mi1poFwCZLJZl9n0DNiokasAZKQ=", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/p-map/download/p-map-4.0.0.tgz", + "integrity": "sha1-uy+Vpe2i7BaOySdOBqdHw+KQTSs=", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/p-try/download/p-try-2.2.0.tgz", + "integrity": "sha1-yyhoVA4xPWHeWPr741zpAE1VQOY=", + "dev": true + }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npm.taobao.org/package-json/download/package-json-6.5.0.tgz", + "integrity": "sha1-b+7ayjXnVyWHbQsOZJdGl/7RRbA=", + "dev": true, + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npm.taobao.org/semver/download/semver-6.3.0.tgz?cache=0&sync_timestamp=1599828351539&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-6.3.0.tgz", + "integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=", + "dev": true + } + } + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npm.taobao.org/pako/download/pako-1.0.11.tgz", + "integrity": "sha1-bJWZ00DVTf05RjgCUqNXBaa5kr8=", + "dev": true + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/parallel-transform/download/parallel-transform-1.2.0.tgz", + "integrity": "sha1-kEnKN9bLIYLDsdLHIL6U0UpYFPw=", + "dev": true, + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npm.taobao.org/parse-asn1/download/parse-asn1-5.1.6.tgz?cache=0&sync_timestamp=1597167309380&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fparse-asn1%2Fdownload%2Fparse-asn1-5.1.6.tgz", + "integrity": "sha1-OFCAo+wTy2KmLTlAnLPoiETNrtQ=", + "dev": true, + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-author": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/parse-author/download/parse-author-2.0.0.tgz", + "integrity": "sha1-00YL8d3Q367tQtp1QkLmX7aEqB8=", + "dev": true, + "requires": { + "author-regex": "^1.0.0" + } + }, + "parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/parse-cache-control/download/parse-cache-control-1.0.1.tgz", + "integrity": "sha1-juqz5U+laSD+Fro493+iGqzC104=" + }, + "parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/parse-entities/download/parse-entities-2.0.0.tgz", + "integrity": "sha1-U8brW5MUofTsmfoP33zgHs2gy+g=", + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/parse-json/download/parse-json-2.2.0.tgz?cache=0&sync_timestamp=1598129182781&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fparse-json%2Fdownload%2Fparse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/parse-passwd/download/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npm.taobao.org/pascalcase/download/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npm.taobao.org/path-browserify/download/path-browserify-0.0.1.tgz", + "integrity": "sha1-5sTd1+06onxoogzE5Q4aTug7vEo=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/path-dirname/download/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true, + "optional": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "http://registry.npm.taobao.org/path-exists/download/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/path-is-absolute/download/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/path-key/download/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "http://registry.npm.taobao.org/path-parse/download/path-parse-1.0.6.tgz", + "integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=", + "dev": true + }, + "path-type": { + "version": "2.0.0", + "resolved": "http://registry.npm.taobao.org/path-type/download/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npm.taobao.org/pify/download/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/pbkdf2/download/pbkdf2-3.1.1.tgz", + "integrity": "sha1-y4cksPramEWWhW0abrr9NYRlS5Q=", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "http://registry.npm.taobao.org/pend/download/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "http://registry.npm.taobao.org/performance-now/download/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npm.taobao.org/picomatch/download/picomatch-2.2.2.tgz", + "integrity": "sha1-IfMz6ba46v8CRo9RRupAbTRfTa0=", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npm.taobao.org/pify/download/pify-4.0.1.tgz", + "integrity": "sha1-SyzSXFDVmHNcUCkiJP2MbfQeMjE=", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/pkg-dir/download/pkg-dir-3.0.0.tgz", + "integrity": "sha1-J0kCDyOe2ZCIGx9xIQ1R62UjvqM=", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "plist": { + "version": "3.0.1", + "resolved": "http://registry.npm.taobao.org/plist/download/plist-3.0.1.tgz", + "integrity": "sha1-qbkx0XwwTokS7wujvdYYK68uH4w=", + "dev": true, + "requires": { + "base64-js": "^1.2.3", + "xmlbuilder": "^9.0.7", + "xmldom": "0.1.x" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "http://registry.npm.taobao.org/posix-character-classes/download/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-7.0.35.tgz?cache=0&sync_timestamp=1601330112363&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss%2Fdownload%2Fpostcss-7.0.35.tgz", + "integrity": "sha1-0r4AuZj38hHYonaXQHny6SuXDiQ=", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "http://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-6.1.0.tgz?cache=0&sync_timestamp=1598611709087&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-6.1.0.tgz", + "integrity": "sha1-B2Srxpxj1ayELdSGfo0CXogN+PM=", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/postcss-modules-extract-imports/download/postcss-modules-extract-imports-2.0.0.tgz?cache=0&sync_timestamp=1600776097223&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-modules-extract-imports%2Fdownload%2Fpostcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha1-gYcZoa4doyX5gyRGsBE27rSTzX4=", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-modules-local-by-default": { + "version": "3.0.3", + "resolved": "https://registry.npm.taobao.org/postcss-modules-local-by-default/download/postcss-modules-local-by-default-3.0.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-modules-local-by-default%2Fdownload%2Fpostcss-modules-local-by-default-3.0.3.tgz", + "integrity": "sha1-uxTgzHgnnVBNvcv9fgyiiZP/u7A=", + "dev": true, + "requires": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/postcss-modules-scope/download/postcss-modules-scope-2.2.0.tgz?cache=0&sync_timestamp=1600777978458&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-modules-scope%2Fdownload%2Fpostcss-modules-scope-2.2.0.tgz", + "integrity": "sha1-OFyuATzHdD9afXYC0Qc6iequYu4=", + "dev": true, + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + } + }, + "postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/postcss-modules-values/download/postcss-modules-values-3.0.0.tgz?cache=0&sync_timestamp=1602187122469&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-modules-values%2Fdownload%2Fpostcss-modules-values-3.0.0.tgz", + "integrity": "sha1-W1AA1uuuKbQlUwG0o6VFdEI+fxA=", + "dev": true, + "requires": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" + } + }, + "postcss-selector-parser": { + "version": "6.0.4", + "resolved": "https://registry.npm.taobao.org/postcss-selector-parser/download/postcss-selector-parser-6.0.4.tgz?cache=0&sync_timestamp=1601045316432&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-selector-parser%2Fdownload%2Fpostcss-selector-parser-6.0.4.tgz", + "integrity": "sha1-VgdaE4CgRgTDiwY+p3Z6Epr1wrM=", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/postcss-value-parser/download/postcss-value-parser-4.1.0.tgz", + "integrity": "sha1-RD9qIM7WSBor2k+oUypuVdeJoss=", + "dev": true + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/prepend-http/download/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "http://registry.npm.taobao.org/process/download/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/process-nextick-args/download/process-nextick-args-2.0.1.tgz", + "integrity": "sha1-eCDZsWEgzFXKmud5JoCufbptf+I=" + }, + "progress": { + "version": "2.0.3", + "resolved": "http://registry.npm.taobao.org/progress/download/progress-2.0.3.tgz", + "integrity": "sha1-foz42PW48jnBvGi+tOt4Vn1XLvg=", + "dev": true + }, + "promise": { + "version": "8.1.0", + "resolved": "https://registry.npm.taobao.org/promise/download/promise-8.1.0.tgz", + "integrity": "sha1-aXwlw9/nQ13Xn81Yw4oTWIjq8F4=", + "requires": { + "asap": "~2.0.6" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/promise-inflight/download/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "prop-types": { + "version": "15.7.2", + "resolved": "http://registry.npm.taobao.org/prop-types/download/prop-types-15.7.2.tgz", + "integrity": "sha1-UsQedbjIfnK52TYOAga5ncv/psU=", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "proto-list": { + "version": "1.2.4", + "resolved": "http://registry.npm.taobao.org/proto-list/download/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "dev": true, + "optional": true + }, + "prr": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/prr/download/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "http://registry.npm.taobao.org/public-encrypt/download/public-encrypt-4.0.3.tgz", + "integrity": "sha1-T8ydd6B+SLp1J+fL4N4z0HATMeA=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.11.9.tgz", + "integrity": "sha1-JtVWgpRY+dHoH8SJUkk9C6NQeCg=", + "dev": true + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "http://registry.npm.taobao.org/pump/download/pump-3.0.0.tgz", + "integrity": "sha1-tKIRaBW94vTh6mAjVOjHVWUQemQ=", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npm.taobao.org/pumpify/download/pumpify-1.5.1.tgz", + "integrity": "sha1-NlE74karJ1cLGjdKXOJ4v9dDcM4=", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "http://registry.npm.taobao.org/pump/download/pump-2.0.1.tgz", + "integrity": "sha1-Ejma3W5M91Jtlzy8i1zi4pCLOQk=", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "http://registry.npm.taobao.org/punycode/download/punycode-2.1.1.tgz", + "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", + "dev": true + }, + "pupa": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/pupa/download/pupa-2.1.1.tgz?cache=0&sync_timestamp=1603649098854&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpupa%2Fdownload%2Fpupa-2.1.1.tgz", + "integrity": "sha1-9ej9SvwsXZeCj6pSNUnth0SiDWI=", + "dev": true, + "requires": { + "escape-goat": "^2.0.0" + } + }, + "qs": { + "version": "6.9.4", + "resolved": "https://registry.npm.taobao.org/qs/download/qs-6.9.4.tgz", + "integrity": "sha1-kJCykNH5FyjTwi5UhDykSupatoc=" + }, + "querystring": { + "version": "0.2.0", + "resolved": "http://registry.npm.taobao.org/querystring/download/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "http://registry.npm.taobao.org/querystring-es3/download/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "raf": { + "version": "3.4.1", + "resolved": "http://registry.npm.taobao.org/raf/download/raf-3.4.1.tgz", + "integrity": "sha1-B0LpmkplUvRF1z4+4DKK8P8e3jk=", + "requires": { + "performance-now": "^2.1.0" + } + }, + "ramda": { + "version": "0.27.1", + "resolved": "https://registry.npm.taobao.org/ramda/download/ramda-0.27.1.tgz?cache=0&sync_timestamp=1596096917227&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Framda%2Fdownload%2Framda-0.27.1.tgz", + "integrity": "sha1-Zvwt8++HOHT/wtpqqJhGWKus9ck=" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "http://registry.npm.taobao.org/randombytes/download/randombytes-2.1.0.tgz", + "integrity": "sha1-32+ENy8CcNxlzfYpE0mrekc9Tyo=", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "http://registry.npm.taobao.org/randomfill/download/randomfill-1.0.4.tgz", + "integrity": "sha1-ySGW/IarQr6YPxvzF3giSTHWFFg=", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npm.taobao.org/rc/download/rc-1.2.8.tgz", + "integrity": "sha1-zZJL9SAKB1uDwYjNa54hG3/A0+0=", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "rc-align": { + "version": "4.0.8", + "resolved": "https://registry.npm.taobao.org/rc-align/download/rc-align-4.0.8.tgz?cache=0&sync_timestamp=1600919919219&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-align%2Fdownload%2Frc-align-4.0.8.tgz", + "integrity": "sha1-J2w/XfrfDeS7lTksuBVoyelHpmg=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "dom-align": "^1.7.0", + "rc-util": "^5.3.0", + "resize-observer-polyfill": "^1.5.1" + } + }, + "rc-animate": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/rc-animate/download/rc-animate-3.1.1.tgz?cache=0&sync_timestamp=1601019718880&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-animate%2Fdownload%2Frc-animate-3.1.1.tgz", + "integrity": "sha1-3v3YY/VoFsIiU05Nxo/t3s0IE4Y=", + "requires": { + "@ant-design/css-animation": "^1.7.2", + "classnames": "^2.2.6", + "raf": "^3.4.0", + "rc-util": "^4.15.3" + }, + "dependencies": { + "rc-util": { + "version": "4.21.1", + "resolved": "https://registry.npm.taobao.org/rc-util/download/rc-util-4.21.1.tgz", + "integrity": "sha1-iGAtDDGFAgqhBT2aHnDqwWG+ywU=", + "requires": { + "add-dom-event-listener": "^1.1.0", + "prop-types": "^15.5.10", + "react-is": "^16.12.0", + "react-lifecycles-compat": "^3.0.4", + "shallowequal": "^1.1.0" + } + } + } + }, + "rc-cascader": { + "version": "1.4.0", + "resolved": "https://registry.npm.taobao.org/rc-cascader/download/rc-cascader-1.4.0.tgz?cache=0&sync_timestamp=1600350720236&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-cascader%2Fdownload%2Frc-cascader-1.4.0.tgz", + "integrity": "sha1-1zHqjgdDNVhieUEDYJGigg6JVHQ=", + "requires": { + "array-tree-filter": "^2.1.0", + "rc-trigger": "^5.0.4", + "rc-util": "^5.0.1", + "warning": "^4.0.1" + } + }, + "rc-checkbox": { + "version": "2.3.1", + "resolved": "https://registry.npm.taobao.org/rc-checkbox/download/rc-checkbox-2.3.1.tgz?cache=0&sync_timestamp=1594779821781&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-checkbox%2Fdownload%2Frc-checkbox-2.3.1.tgz", + "integrity": "sha1-KmG8QwF8eDvS6fGmdVO/jv56pNM=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1" + } + }, + "rc-collapse": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/rc-collapse/download/rc-collapse-2.0.1.tgz?cache=0&sync_timestamp=1600493654424&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-collapse%2Fdownload%2Frc-collapse-2.0.1.tgz", + "integrity": "sha1-medlWs2cI3tyNpo53LXHE0UeHpI=", + "requires": { + "@ant-design/css-animation": "^1.7.2", + "classnames": "2.x", + "rc-animate": "3.x", + "rc-util": "^5.2.1", + "shallowequal": "^1.1.0" + } + }, + "rc-dialog": { + "version": "8.2.2", + "resolved": "https://registry.npm.taobao.org/rc-dialog/download/rc-dialog-8.2.2.tgz?cache=0&sync_timestamp=1601352025965&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-dialog%2Fdownload%2Frc-dialog-8.2.2.tgz", + "integrity": "sha1-/pdHlXXsMLufdSQanym/OdcWNsY=", + "requires": { + "@babel/runtime": "^7.10.1", + "rc-animate": "3.x", + "rc-util": "^5.0.1" + } + }, + "rc-drawer": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/rc-drawer/download/rc-drawer-4.1.0.tgz?cache=0&sync_timestamp=1591625663134&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-drawer%2Fdownload%2Frc-drawer-4.1.0.tgz", + "integrity": "sha1-178LwDAwC2LSgrwE4FO5rK1rCLQ=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.6", + "rc-util": "^5.0.1" + } + }, + "rc-dropdown": { + "version": "3.2.0", + "resolved": "https://registry.npm.taobao.org/rc-dropdown/download/rc-dropdown-3.2.0.tgz?cache=0&sync_timestamp=1600332823107&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-dropdown%2Fdownload%2Frc-dropdown-3.2.0.tgz", + "integrity": "sha1-2mwq2kA4QrruOp6Qmgsakbo+EJA=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.6", + "rc-trigger": "^5.0.4" + } + }, + "rc-field-form": { + "version": "1.10.1", + "resolved": "https://registry.npm.taobao.org/rc-field-form/download/rc-field-form-1.10.1.tgz", + "integrity": "sha1-9ut2tfJLWJOOut/APN2BTCTefbM=", + "requires": { + "@babel/runtime": "^7.8.4", + "async-validator": "^3.0.3", + "rc-util": "^5.0.0" + } + }, + "rc-image": { + "version": "3.0.6", + "resolved": "https://registry.npm.taobao.org/rc-image/download/rc-image-3.0.6.tgz?cache=0&sync_timestamp=1601432322080&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-image%2Fdownload%2Frc-image-3.0.6.tgz", + "integrity": "sha1-sa0xRj0MwhuO5qC83DkbNaOEudU=", + "requires": { + "@ant-design/icons": "^4.2.2", + "@babel/runtime": "^7.11.2", + "classnames": "^2.2.6", + "rc-dialog": "~8.2.2", + "rc-util": "^5.0.6" + } + }, + "rc-input-number": { + "version": "6.0.1", + "resolved": "https://registry.npm.taobao.org/rc-input-number/download/rc-input-number-6.0.1.tgz", + "integrity": "sha1-UdejdipOrsCRZm4VPgBW83KSLcU=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.0.1" + } + }, + "rc-mentions": { + "version": "1.5.2", + "resolved": "https://registry.npm.taobao.org/rc-mentions/download/rc-mentions-1.5.2.tgz?cache=0&sync_timestamp=1601390471130&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-mentions%2Fdownload%2Frc-mentions-1.5.2.tgz", + "integrity": "sha1-lFWaNp3nPnzJLzQ7ra+USZ+0EKc=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.6", + "rc-menu": "^8.0.1", + "rc-textarea": "^0.3.0", + "rc-trigger": "^5.0.4", + "rc-util": "^5.0.1" + } + }, + "rc-menu": { + "version": "8.7.1", + "resolved": "https://registry.npm.taobao.org/rc-menu/download/rc-menu-8.7.1.tgz?cache=0&sync_timestamp=1600394732388&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-menu%2Fdownload%2Frc-menu-8.7.1.tgz", + "integrity": "sha1-89yuslMIVooHhxt5+N3ZUVpk6L4=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "mini-store": "^3.0.1", + "omit.js": "^2.0.0", + "rc-motion": "^2.0.1", + "rc-trigger": "^5.0.4", + "rc-util": "^5.0.1", + "resize-observer-polyfill": "^1.5.0", + "shallowequal": "^1.1.0" + } + }, + "rc-motion": { + "version": "2.3.2", + "resolved": "https://registry.npm.taobao.org/rc-motion/download/rc-motion-2.3.2.tgz?cache=0&sync_timestamp=1602234025899&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-motion%2Fdownload%2Frc-motion-2.3.2.tgz", + "integrity": "sha1-DAcLZOn5lK8vrSweDDbyjTh1LSk=", + "requires": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.2.1" + } + }, + "rc-notification": { + "version": "4.4.0", + "resolved": "https://registry.npm.taobao.org/rc-notification/download/rc-notification-4.4.0.tgz?cache=0&sync_timestamp=1601218928374&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-notification%2Fdownload%2Frc-notification-4.4.0.tgz", + "integrity": "sha1-GS0ILNbimVcF9DxpKRYmMccePbE=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-animate": "3.x", + "rc-util": "^5.0.1" + } + }, + "rc-pagination": { + "version": "3.0.4", + "resolved": "https://registry.npm.taobao.org/rc-pagination/download/rc-pagination-3.0.4.tgz?cache=0&sync_timestamp=1602214455087&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-pagination%2Fdownload%2Frc-pagination-3.0.4.tgz", + "integrity": "sha1-MlCfzfdzh8oSTAJcNXXnurDH3r0=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1" + } + }, + "rc-picker": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/rc-picker/download/rc-picker-2.1.0.tgz?cache=0&sync_timestamp=1602223222168&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-picker%2Fdownload%2Frc-picker-2.1.0.tgz", + "integrity": "sha1-kUccxb+MG8Tkivo39ASAaCIvP1A=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "date-fns": "^2.15.0", + "dayjs": "^1.8.30", + "moment": "^2.24.0", + "rc-trigger": "^5.0.4", + "rc-util": "^5.0.1", + "shallowequal": "^1.1.0" + } + }, + "rc-progress": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/rc-progress/download/rc-progress-3.1.0.tgz", + "integrity": "sha1-zUpaeFPbPgwQkYrSwXBoimjjzr0=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.6" + } + }, + "rc-rate": { + "version": "2.8.2", + "resolved": "https://registry.npm.taobao.org/rc-rate/download/rc-rate-2.8.2.tgz?cache=0&sync_timestamp=1592197815194&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-rate%2Fdownload%2Frc-rate-2.8.2.tgz", + "integrity": "sha1-2C0jfXT9Su8+BYHScAtkbN0c2KI=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.0.1" + } + }, + "rc-resize-observer": { + "version": "0.2.5", + "resolved": "https://registry.npm.taobao.org/rc-resize-observer/download/rc-resize-observer-0.2.5.tgz", + "integrity": "sha1-A+Olw9/M1smWpUfk+Cch5PIPYVY=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "rc-util": "^5.0.0", + "resize-observer-polyfill": "^1.5.1" + } + }, + "rc-select": { + "version": "11.3.3", + "resolved": "https://registry.npm.taobao.org/rc-select/download/rc-select-11.3.3.tgz?cache=0&sync_timestamp=1602165889338&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-select%2Fdownload%2Frc-select-11.3.3.tgz", + "integrity": "sha1-ukRaxNLZM90fgLeWwd4ozmyBu/g=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-trigger": "^5.0.4", + "rc-util": "^5.0.1", + "rc-virtual-list": "^3.0.3", + "warning": "^4.0.3" + } + }, + "rc-slider": { + "version": "9.5.3", + "resolved": "https://registry.npm.taobao.org/rc-slider/download/rc-slider-9.5.3.tgz?cache=0&sync_timestamp=1602213380778&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-slider%2Fdownload%2Frc-slider-9.5.3.tgz", + "integrity": "sha1-rxGK3RQU32jdNTdGAEA2zCuDkkE=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-tooltip": "^5.0.1", + "rc-util": "^5.0.0", + "shallowequal": "^1.1.0" + } + }, + "rc-steps": { + "version": "4.1.2", + "resolved": "https://registry.npm.taobao.org/rc-steps/download/rc-steps-4.1.2.tgz", + "integrity": "sha1-Nw9Ma0DTiI8DsnHxYolTxm65HAQ=", + "requires": { + "@babel/runtime": "^7.10.2", + "classnames": "^2.2.3", + "rc-util": "^5.0.1" + } + }, + "rc-switch": { + "version": "3.2.1", + "resolved": "https://registry.npm.taobao.org/rc-switch/download/rc-switch-3.2.1.tgz?cache=0&sync_timestamp=1598245147149&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-switch%2Fdownload%2Frc-switch-3.2.1.tgz", + "integrity": "sha1-d1+LLIZxZDHYPSsT5HEIFbqv7eA=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "rc-util": "^5.0.1" + } + }, + "rc-table": { + "version": "7.9.10", + "resolved": "https://registry.npm.taobao.org/rc-table/download/rc-table-7.9.10.tgz?cache=0&sync_timestamp=1602299426567&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-table%2Fdownload%2Frc-table-7.9.10.tgz", + "integrity": "sha1-79WtDmQ6Bu7ysLfrnwe9VeD/UbU=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "raf": "^3.4.1", + "rc-resize-observer": "^0.2.0", + "rc-util": "^5.0.4", + "shallowequal": "^1.1.0" + } + }, + "rc-tabs": { + "version": "11.6.2", + "resolved": "https://registry.npm.taobao.org/rc-tabs/download/rc-tabs-11.6.2.tgz?cache=0&sync_timestamp=1600138991282&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-tabs%2Fdownload%2Frc-tabs-11.6.2.tgz", + "integrity": "sha1-BzKj9u82oUND+a/kfffymXHoAgs=", + "requires": { + "@babel/runtime": "^7.11.2", + "classnames": "2.x", + "raf": "^3.4.1", + "rc-dropdown": "^3.1.3", + "rc-menu": "^8.6.1", + "rc-resize-observer": "^0.2.1", + "rc-util": "^5.0.0" + } + }, + "rc-textarea": { + "version": "0.3.0", + "resolved": "https://registry.npm.taobao.org/rc-textarea/download/rc-textarea-0.3.0.tgz?cache=0&sync_timestamp=1594206223732&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-textarea%2Fdownload%2Frc-textarea-0.3.0.tgz", + "integrity": "sha1-mGDveX4AcX2CJ9HvTueJXdk1jd8=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "omit.js": "^2.0.0", + "rc-resize-observer": "^0.2.3" + } + }, + "rc-tooltip": { + "version": "5.0.1", + "resolved": "https://registry.npm.taobao.org/rc-tooltip/download/rc-tooltip-5.0.1.tgz?cache=0&sync_timestamp=1600053419732&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-tooltip%2Fdownload%2Frc-tooltip-5.0.1.tgz", + "integrity": "sha1-uCxCWWBNLLYsphDteTLdN/xu9h0=", + "requires": { + "@babel/runtime": "^7.11.2", + "rc-trigger": "^5.0.0" + } + }, + "rc-tree": { + "version": "3.10.0", + "resolved": "https://registry.npm.taobao.org/rc-tree/download/rc-tree-3.10.0.tgz?cache=0&sync_timestamp=1600393754901&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-tree%2Fdownload%2Frc-tree-3.10.0.tgz", + "integrity": "sha1-iXSYs3VvbIT0GtKyRO6Uiav0O38=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-util": "^5.0.0", + "rc-virtual-list": "^3.0.1" + } + }, + "rc-tree-select": { + "version": "4.1.2", + "resolved": "https://registry.npm.taobao.org/rc-tree-select/download/rc-tree-select-4.1.2.tgz?cache=0&sync_timestamp=1598868328963&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-tree-select%2Fdownload%2Frc-tree-select-4.1.2.tgz", + "integrity": "sha1-vwEsPDLPLoL8f/vdYMtZYWOikKA=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-select": "^11.1.1", + "rc-tree": "^3.8.0", + "rc-util": "^5.0.5" + } + }, + "rc-trigger": { + "version": "5.0.6", + "resolved": "https://registry.npm.taobao.org/rc-trigger/download/rc-trigger-5.0.6.tgz?cache=0&sync_timestamp=1601013795310&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-trigger%2Fdownload%2Frc-trigger-5.0.6.tgz", + "integrity": "sha1-foRxdSWHGnkjpnHttaKQ6eYWUls=", + "requires": { + "@babel/runtime": "^7.11.2", + "classnames": "^2.2.6", + "rc-align": "^4.0.0", + "rc-motion": "^2.0.0", + "rc-util": "^5.3.4" + } + }, + "rc-upload": { + "version": "3.3.1", + "resolved": "https://registry.npm.taobao.org/rc-upload/download/rc-upload-3.3.1.tgz?cache=0&sync_timestamp=1599448564719&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-upload%2Fdownload%2Frc-upload-3.3.1.tgz", + "integrity": "sha1-rYZYsqeWAxkws10rB6sxK3zUye0=", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.2.0" + } + }, + "rc-util": { + "version": "5.4.0", + "resolved": "https://registry.npm.taobao.org/rc-util/download/rc-util-5.4.0.tgz", + "integrity": "sha1-aI6u7P2una4r/fEL7b6IRZHboAQ=", + "requires": { + "react-is": "^16.12.0", + "shallowequal": "^1.1.0" + } + }, + "rc-virtual-list": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/rc-virtual-list/download/rc-virtual-list-3.1.1.tgz", + "integrity": "sha1-5eF8kLUW8V2d6buSdHD0avpr3qs=", + "requires": { + "classnames": "^2.2.6", + "rc-resize-observer": "^0.2.3", + "rc-util": "^5.0.7" + } + }, + "rcedit": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/rcedit/download/rcedit-2.2.0.tgz", + "integrity": "sha1-O/5xJIdJ+oIf7ybto9NfTg7kevw=", + "dev": true + }, + "react": { + "version": "16.13.1", + "resolved": "https://registry.npm.taobao.org/react/download/react-16.13.1.tgz?cache=0&sync_timestamp=1602081907934&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact%2Fdownload%2Freact-16.13.1.tgz", + "integrity": "sha1-LoGIIvGpdDEiwGPWQQ2FweOv5I4=", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-confirm-alert": { + "version": "2.6.2", + "resolved": "https://registry.npm.taobao.org/react-confirm-alert/download/react-confirm-alert-2.6.2.tgz", + "integrity": "sha1-d1xuUjPNLEsG9N3t1ZuN4D4HGHY=" + }, + "react-dom": { + "version": "16.13.1", + "resolved": "https://registry.npm.taobao.org/react-dom/download/react-dom-16.13.1.tgz", + "integrity": "sha1-wb03MxoEhsB47lTEdAcgmTsuDn8=", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npm.taobao.org/react-is/download/react-is-16.13.1.tgz?cache=0&sync_timestamp=1602081834670&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-is%2Fdownload%2Freact-is-16.13.1.tgz", + "integrity": "sha1-eJcppNw23imZ3BVt1sHZwYzqVqQ=" + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "http://registry.npm.taobao.org/react-lifecycles-compat/download/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha1-TxonOv38jzSIqMUWv9p4+HI1I2I=" + }, + "react-markdown": { + "version": "5.0.2", + "resolved": "https://registry.npm.taobao.org/react-markdown/download/react-markdown-5.0.2.tgz?cache=0&sync_timestamp=1603437055325&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-markdown%2Fdownload%2Freact-markdown-5.0.2.tgz", + "integrity": "sha1-0VqL6ze07DT8I92JLndV63BAuNs=", + "requires": { + "@types/mdast": "^3.0.3", + "@types/unist": "^2.0.3", + "html-to-react": "^1.3.4", + "mdast-add-list-metadata": "1.0.1", + "prop-types": "^15.7.2", + "react-is": "^16.8.6", + "remark-parse": "^9.0.0", + "unified": "^9.0.0", + "unist-util-visit": "^2.0.0", + "xtend": "^4.0.1" + } + }, + "read-config-file": { + "version": "6.0.0", + "resolved": "https://registry.npm.taobao.org/read-config-file/download/read-config-file-6.0.0.tgz", + "integrity": "sha1-Iktdympb3B+xnmP4nzQmgO/bkpk=", + "dev": true, + "requires": { + "dotenv": "^8.2.0", + "dotenv-expand": "^5.1.0", + "js-yaml": "^3.13.1", + "json5": "^2.1.2", + "lazy-val": "^1.0.4" + }, + "dependencies": { + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npm.taobao.org/json5/download/json5-2.1.3.tgz", + "integrity": "sha1-ybD3+pIzv+WAf+ZvzzpWF+1ZfUM=", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + } + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/read-pkg/download/read-pkg-2.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fread-pkg%2Fdownload%2Fread-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/read-pkg-up/download/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/find-up/download/find-up-2.1.0.tgz?cache=0&sync_timestamp=1597169842138&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffind-up%2Fdownload%2Ffind-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/locate-path/download/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npm.taobao.org/p-limit/download/p-limit-1.3.0.tgz", + "integrity": "sha1-uGvV8MJWkJEcdZD8v8IBDVSzzLg=", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/p-locate/download/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/p-try/download/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + } + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npm.taobao.org/readable-stream/download/readable-stream-2.3.7.tgz?cache=0&sync_timestamp=1581624324274&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freadable-stream%2Fdownload%2Freadable-stream-2.3.7.tgz", + "integrity": "sha1-Hsoc9xGu+BTAT2IlKjamL2yyO1c=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npm.taobao.org/readdirp/download/readdirp-3.4.0.tgz", + "integrity": "sha1-n9zN+ekVWAVEkiGsZF6DA6tbmto=", + "dev": true, + "optional": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.13.7.tgz?cache=0&sync_timestamp=1595456311465&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fregenerator-runtime%2Fdownload%2Fregenerator-runtime-0.13.7.tgz", + "integrity": "sha1-ysLazIoepnX+qrrriugziYrkb1U=" + }, + "regex-not": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/regex-not/download/regex-not-1.0.2.tgz", + "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "registry-auth-token": { + "version": "4.2.1", + "resolved": "https://registry.npm.taobao.org/registry-auth-token/download/registry-auth-token-4.2.1.tgz?cache=0&sync_timestamp=1605012436264&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fregistry-auth-token%2Fdownload%2Fregistry-auth-token-4.2.1.tgz", + "integrity": "sha1-bXtABkQZGJcszV/tzUHcMix5slA=", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npm.taobao.org/registry-url/download/registry-url-5.1.0.tgz", + "integrity": "sha1-6YM0tQ1UNLgRNrROxjjZwgCcUAk=", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "remark-parse": { + "version": "9.0.0", + "resolved": "https://registry.npm.taobao.org/remark-parse/download/remark-parse-9.0.0.tgz?cache=0&sync_timestamp=1602663704570&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fremark-parse%2Fdownload%2Fremark-parse-9.0.0.tgz", + "integrity": "sha1-TSCimWZYgOT0r12Qt8e4qTWFNkA=", + "requires": { + "mdast-util-from-markdown": "^0.8.0" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "http://registry.npm.taobao.org/remove-trailing-separator/download/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true, + "optional": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "http://registry.npm.taobao.org/repeat-element/download/repeat-element-1.1.3.tgz", + "integrity": "sha1-eC4NglwMWjuzlzH4Tv7mt0Lmsc4=", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "http://registry.npm.taobao.org/repeat-string/download/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/replace-ext/download/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" + }, + "require-directory": { + "version": "2.1.1", + "resolved": "http://registry.npm.taobao.org/require-directory/download/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "http://registry.npm.taobao.org/require-main-filename/download/require-main-filename-2.0.0.tgz", + "integrity": "sha1-0LMp7MfMD2Fkn2IhW+aa9UqomJs=", + "dev": true + }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "http://registry.npm.taobao.org/resize-observer-polyfill/download/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha1-DpAg3T0hAkRY1OvSfiPkAmmBBGQ=" + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npm.taobao.org/resolve/download/resolve-1.17.0.tgz?cache=0&sync_timestamp=1589682751623&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fresolve%2Fdownload%2Fresolve-1.17.0.tgz", + "integrity": "sha1-sllBtUloIxzC0bt2p5y38sC/hEQ=", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/resolve-cwd/download/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/resolve-dir/download/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "dependencies": { + "global-modules": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/global-modules/download/global-modules-1.0.0.tgz", + "integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + } + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "http://registry.npm.taobao.org/resolve-from/download/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npm.taobao.org/resolve-url/download/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/responselike/download/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "http://registry.npm.taobao.org/ret/download/ret-0.1.15.tgz", + "integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/reusify/download/reusify-1.0.4.tgz", + "integrity": "sha1-kNo4Kx4SbvwCFG6QhFqI2xKSXXY=", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npm.taobao.org/rimraf/download/rimraf-3.0.2.tgz?cache=0&sync_timestamp=1581257110269&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frimraf%2Fdownload%2Frimraf-3.0.2.tgz", + "integrity": "sha1-8aVAK6YiCtUswSgrrBrjqkn9Bho=", + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "http://registry.npm.taobao.org/ripemd160/download/ripemd160-2.0.2.tgz", + "integrity": "sha1-ocGm9iR1FXe6XQeRTLyShQWFiQw=", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "roarr": { + "version": "2.15.4", + "resolved": "https://registry.npm.taobao.org/roarr/download/roarr-2.15.4.tgz?cache=0&sync_timestamp=1600094609360&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Froarr%2Fdownload%2Froarr-2.15.4.tgz", + "integrity": "sha1-9f55W3uDjM/jXcYI4Cgrnrouev0=", + "dev": true, + "optional": true, + "requires": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + } + }, + "rollup": { + "version": "1.32.1", + "resolved": "https://registry.npm.taobao.org/rollup/download/rollup-1.32.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frollup%2Fdownload%2Frollup-1.32.1.tgz", + "integrity": "sha1-RIDlLZ2eKuS0a6DZ3erzFjlA+cQ=", + "requires": { + "@types/estree": "*", + "@types/node": "*", + "acorn": "^7.1.0" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npm.taobao.org/acorn/download/acorn-7.4.1.tgz?cache=0&sync_timestamp=1601885779818&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Facorn%2Fdownload%2Facorn-7.4.1.tgz", + "integrity": "sha1-/q7SVZc9LndVW4PbwIhRpsY1IPo=" + } + } + }, + "run-parallel": { + "version": "1.1.10", + "resolved": "https://registry.npm.taobao.org/run-parallel/download/run-parallel-1.1.10.tgz", + "integrity": "sha1-YKUbKug2Y2yBN33xbLEHNRvNE+8=", + "dev": true + }, + "run-queue": { + "version": "1.0.3", + "resolved": "http://registry.npm.taobao.org/run-queue/download/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.2.tgz", + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/safe-regex/download/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "http://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz", + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" + }, + "sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npm.taobao.org/sanitize-filename/download/sanitize-filename-1.6.3.tgz", + "integrity": "sha1-dV69dSBFkxl34wsgJdNA18kJA3g=", + "requires": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npm.taobao.org/sax/download/sax-1.2.4.tgz", + "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=", + "dev": true + }, + "scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npm.taobao.org/scheduler/download/scheduler-0.19.1.tgz?cache=0&sync_timestamp=1602081889953&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fscheduler%2Fdownload%2Fscheduler-0.19.1.tgz", + "integrity": "sha1-Tz4u0sGn1laB9MhU+oxaHMtA8ZY=", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-1.0.0.tgz", + "integrity": "sha1-C3mpMgTXtgDUsoUNH2bCo0lRx3A=", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "scroll-into-view-if-needed": { + "version": "2.2.26", + "resolved": "https://registry.npm.taobao.org/scroll-into-view-if-needed/download/scroll-into-view-if-needed-2.2.26.tgz", + "integrity": "sha1-5JF9oMggE1/2Wtb35LfXr1aMTxM=", + "requires": { + "compute-scroll-into-view": "^1.0.16" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npm.taobao.org/semver/download/semver-5.7.1.tgz", + "integrity": "sha1-qVT5Ma66UI0we78Gnv8MAclhFvc=", + "dev": true + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/semver-compare/download/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true, + "optional": true + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/semver-diff/download/semver-diff-3.1.1.tgz", + "integrity": "sha1-Bfd85Z8yXgDicGr9Z7tQbdscoys=", + "dev": true, + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npm.taobao.org/semver/download/semver-6.3.0.tgz?cache=0&sync_timestamp=1599828351539&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-6.3.0.tgz", + "integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=", + "dev": true + } + } + }, + "serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npm.taobao.org/serialize-error/download/serialize-error-7.0.1.tgz", + "integrity": "sha1-8TYLBEf2H/tIPsQVfHN/q313jhg=", + "dev": true, + "optional": true, + "requires": { + "type-fest": "^0.13.1" + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/serialize-javascript/download/serialize-javascript-4.0.0.tgz?cache=0&sync_timestamp=1599740650381&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fserialize-javascript%2Fdownload%2Fserialize-javascript-4.0.0.tgz", + "integrity": "sha1-tSXhI4SJpez8Qq+sw/6Z5mb0sao=", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "http://registry.npm.taobao.org/set-blocking/download/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/set-value/download/set-value-2.0.1.tgz", + "integrity": "sha1-oY1AUw5vB95CKMfe/kInr4ytAFs=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "http://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "http://registry.npm.taobao.org/setimmediate/download/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "http://registry.npm.taobao.org/sha.js/download/sha.js-2.4.11.tgz", + "integrity": "sha1-N6XPC4HsvGlD3hCbopYNGyZYSuc=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallowequal": { + "version": "1.1.0", + "resolved": "http://registry.npm.taobao.org/shallowequal/download/shallowequal-1.1.0.tgz", + "integrity": "sha1-GI1SHelbkIdAT9TctosT3wrk5/g=" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/shebang-command/download/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/shebang-regex/download/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shineout": { + "version": "1.6.1", + "resolved": "https://registry.npm.taobao.org/shineout/download/shineout-1.6.1.tgz", + "integrity": "sha1-cGf1X6MG4CHHZ5MComI2t+xOfgo=", + "requires": { + "classnames": "^2.2.5", + "create-react-context": "^0.2.3", + "date-fns": "2.0.0-alpha.10", + "deep-eql": "^4.0.0", + "immer": "^4.0.0" + }, + "dependencies": { + "date-fns": { + "version": "2.0.0-alpha.10", + "resolved": "https://registry.npm.taobao.org/date-fns/download/date-fns-2.0.0-alpha.10.tgz?cache=0&sync_timestamp=1598884225872&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdate-fns%2Fdownload%2Fdate-fns-2.0.0-alpha.10.tgz", + "integrity": "sha1-0zRFOk2OBkPb7i1wQ2/JJiwV2oA=" + } + } + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npm.taobao.org/signal-exit/download/signal-exit-3.0.3.tgz?cache=0&sync_timestamp=1600349108829&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsignal-exit%2Fdownload%2Fsignal-exit-3.0.3.tgz", + "integrity": "sha1-oUEMLt2PB3sItOJTyOrPyvBXRhw=", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/slash/download/slash-3.0.0.tgz", + "integrity": "sha1-ZTm+hwwWWtvVJAIg2+Nh8bxNRjQ=", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "http://registry.npm.taobao.org/snapdragon/download/snapdragon-0.8.2.tgz", + "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "http://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "http://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "http://registry.npm.taobao.org/snapdragon-node/download/snapdragon-node-2.1.1.tgz", + "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/define-property/download/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/is-data-descriptor/download/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/is-descriptor/download/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "http://registry.npm.taobao.org/snapdragon-util/download/snapdragon-util-3.0.1.tgz", + "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "http://registry.npm.taobao.org/source-list-map/download/source-list-map-2.0.1.tgz", + "integrity": "sha1-OZO9hzv8SEecyp6jpUeDXHwVSzQ=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "http://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-loader": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/source-map-loader/download/source-map-loader-1.1.0.tgz", + "integrity": "sha1-8PzIgQYTd5OonsAPEYGWtgHxEa4=", + "dev": true, + "requires": { + "abab": "^2.0.4", + "iconv-lite": "^0.6.2", + "loader-utils": "^2.0.0", + "schema-utils": "^2.7.0", + "source-map": "^0.6.1", + "whatwg-mimetype": "^2.3.0" + }, + "dependencies": { + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npm.taobao.org/json5/download/json5-2.1.3.tgz", + "integrity": "sha1-ybD3+pIzv+WAf+ZvzzpWF+1ZfUM=", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-2.0.0.tgz", + "integrity": "sha1-5MrOW4FtQloWa18JfhDNErNgZLA=", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-2.7.1.tgz", + "integrity": "sha1-HKTzLRskxZDCA7jnpQvw6kzTlNc=", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "http://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "dev": true + } + } + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npm.taobao.org/source-map-resolve/download/source-map-resolve-0.5.3.tgz", + "integrity": "sha1-GQhmvs51U+H48mei7oLGBrVQmho=", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npm.taobao.org/source-map-support/download/source-map-support-0.5.19.tgz?cache=0&sync_timestamp=1589682814927&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsource-map-support%2Fdownload%2Fsource-map-support-0.5.19.tgz", + "integrity": "sha1-qYti+G3K9PZzmWSMCFKRq56P7WE=", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "http://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "http://registry.npm.taobao.org/source-map-url/download/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/spdx-correct/download/spdx-correct-3.1.1.tgz", + "integrity": "sha1-3s6BrJweZxPl99G28X1Gj6U9iak=", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npm.taobao.org/spdx-exceptions/download/spdx-exceptions-2.3.0.tgz", + "integrity": "sha1-PyjOGnegA3JoPq3kpDMYNSeiFj0=", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npm.taobao.org/spdx-expression-parse/download/spdx-expression-parse-3.0.1.tgz?cache=0&sync_timestamp=1589682794533&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fspdx-expression-parse%2Fdownload%2Fspdx-expression-parse-3.0.1.tgz", + "integrity": "sha1-z3D1BILu/cmOPOCmgz5KU87rpnk=", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.6", + "resolved": "https://registry.npm.taobao.org/spdx-license-ids/download/spdx-license-ids-3.0.6.tgz?cache=0&sync_timestamp=1600284873714&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fspdx-license-ids%2Fdownload%2Fspdx-license-ids-3.0.6.tgz", + "integrity": "sha1-yAdXODwoq/cpZ0SZjLwQaui4VM4=", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/split-string/download/split-string-3.1.0.tgz", + "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "http://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.1.2.tgz", + "integrity": "sha1-2hdlJiv4wPVxdJ8q1sJjACB65nM=", + "dev": true, + "optional": true + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npm.taobao.org/ssri/download/ssri-6.0.1.tgz", + "integrity": "sha1-KjxBso3UW2K2Nnbst0ABJlrp7dg=", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "stat-mode": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/stat-mode/download/stat-mode-1.0.0.tgz", + "integrity": "sha1-aLVcth6mOf9XE282shaikYANFGU=", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "http://registry.npm.taobao.org/static-extend/download/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "http://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/stream-browserify/download/stream-browserify-2.0.2.tgz", + "integrity": "sha1-h1IdOKRKp+6RzhzSpH3wy0ndZgs=", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "http://registry.npm.taobao.org/stream-each/download/stream-each-1.2.3.tgz", + "integrity": "sha1-6+J6DDibBPvMIzZClS4Qcxr6m64=", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npm.taobao.org/stream-http/download/stream-http-2.8.3.tgz", + "integrity": "sha1-stJCRpKIpaJ+xP6JM6z2I95lFPw=", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/stream-shift/download/stream-shift-1.0.1.tgz", + "integrity": "sha1-1wiCgVWasneEJCebCHfaPDktWj0=", + "dev": true + }, + "string-convert": { + "version": "0.2.1", + "resolved": "http://registry.npm.taobao.org/string-convert/download/string-convert-0.2.1.tgz", + "integrity": "sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/string-width/download/string-width-3.1.0.tgz", + "integrity": "sha1-InZ74htirxCBV0MG9prFG2IgOWE=", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/string_decoder/download/string_decoder-1.1.1.tgz?cache=0&sync_timestamp=1565170823020&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstring_decoder%2Fdownload%2Fstring_decoder-1.1.1.tgz", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-5.2.0.tgz?cache=0&sync_timestamp=1589682795383&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-5.2.0.tgz", + "integrity": "sha1-jJpTb+tq/JYr36WxBKUJHBrZwK4=", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/strip-bom/download/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/strip-json-comments/download/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "strip-outer": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/strip-outer/download/strip-outer-1.0.1.tgz", + "integrity": "sha1-sv0qv2YEudHmATBXGV34Nrip1jE=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, + "style-loader": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/style-loader/download/style-loader-2.0.0.tgz?cache=0&sync_timestamp=1602247951903&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstyle-loader%2Fdownload%2Fstyle-loader-2.0.0.tgz", + "integrity": "sha1-lmlgL9RpB0DqrsE3eZoDrdu8OTw=", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npm.taobao.org/json5/download/json5-2.1.3.tgz", + "integrity": "sha1-ybD3+pIzv+WAf+ZvzzpWF+1ZfUM=", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-2.0.0.tgz", + "integrity": "sha1-5MrOW4FtQloWa18JfhDNErNgZLA=", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-3.0.0.tgz", + "integrity": "sha1-Z1AvaqK2ai1AMrQnmilEl4oJE+8=", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npm.taobao.org/sumchecker/download/sumchecker-3.0.1.tgz", + "integrity": "sha1-Y3fplnlauwttNI6bPh37JDRajkI=", + "dev": true, + "requires": { + "debug": "^4.1.0" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-4.2.0.tgz?cache=0&sync_timestamp=1600502894812&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.2.0.tgz", + "integrity": "sha1-fxUPk5IOlMWPVXTC/QGjEQ7/5/E=", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-5.5.0.tgz?cache=0&sync_timestamp=1598611709087&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-5.5.0.tgz", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "sync-request": { + "version": "6.1.0", + "resolved": "https://registry.npm.taobao.org/sync-request/download/sync-request-6.1.0.tgz", + "integrity": "sha1-6WIXVlteULv/4XmGi6dVMvtZfmg=", + "requires": { + "http-response-object": "^3.0.1", + "sync-rpc": "^1.2.1", + "then-request": "^6.0.0" + } + }, + "sync-rpc": { + "version": "1.3.6", + "resolved": "https://registry.npm.taobao.org/sync-rpc/download/sync-rpc-1.3.6.tgz", + "integrity": "sha1-suiyVQoSzLxx34ZEgQUp3raGZac=", + "requires": { + "get-port": "^3.1.0" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/tapable/download/tapable-1.1.3.tgz?cache=0&sync_timestamp=1600381197118&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftapable%2Fdownload%2Ftapable-1.1.3.tgz", + "integrity": "sha1-ofzMBrWNth/XpF2i2kT186Pme6I=", + "dev": true + }, + "tar": { + "version": "6.0.5", + "resolved": "https://registry.npm.taobao.org/tar/download/tar-6.0.5.tgz", + "integrity": "sha1-vegVCG4Qs58dzSmOidWW4VNeIA8=", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/chownr/download/chownr-2.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchownr%2Fdownload%2Fchownr-2.0.0.tgz", + "integrity": "sha1-Fb++U9LqtM9w8YqM1o6+Wzyx3s4=", + "dev": true + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/mkdirp/download/mkdirp-1.0.4.tgz?cache=0&sync_timestamp=1600349118431&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmkdirp%2Fdownload%2Fmkdirp-1.0.4.tgz", + "integrity": "sha1-PrXtYmInVteaXw4qIh3+utdcL34=", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/yallist/download/yallist-4.0.0.tgz", + "integrity": "sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=", + "dev": true + } + } + }, + "temp-file": { + "version": "3.3.7", + "resolved": "https://registry.npm.taobao.org/temp-file/download/temp-file-3.3.7.tgz", + "integrity": "sha1-aGiF1jX4cnSOOE6HGFWVhHCusYo=", + "dev": true, + "requires": { + "async-exit-hook": "^2.0.1", + "fs-extra": "^8.1.0" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npm.taobao.org/fs-extra/download/fs-extra-8.1.0.tgz", + "integrity": "sha1-SdQ8RaiM2Wd2aMt74bRu/bjS4cA=", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, + "term-size": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/term-size/download/term-size-2.2.1.tgz", + "integrity": "sha1-KmpUhAQywvtjIP6g9BVTHpAYn1Q=", + "dev": true + }, + "terser": { + "version": "4.8.0", + "resolved": "https://registry.npm.taobao.org/terser/download/terser-4.8.0.tgz?cache=0&sync_timestamp=1601639556443&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fterser%2Fdownload%2Fterser-4.8.0.tgz", + "integrity": "sha1-YwVjQ9fHC7KfOvZlhlpG/gOg3xc=", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "http://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "1.4.5", + "resolved": "https://registry.npm.taobao.org/terser-webpack-plugin/download/terser-webpack-plugin-1.4.5.tgz", + "integrity": "sha1-oheu+uozDnNP+sthIOwfoxLWBAs=", + "dev": true, + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "http://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "dev": true + } + } + }, + "then-request": { + "version": "6.0.2", + "resolved": "https://registry.npm.taobao.org/then-request/download/then-request-6.0.2.tgz", + "integrity": "sha1-7Bjdi1ykOq7ly5L35MFjDpUNTww=", + "requires": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^8.1.1", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + }, + "dependencies": { + "@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npm.taobao.org/@types/node/download/@types/node-8.10.66.tgz?cache=0&sync_timestamp=1605819795096&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-8.10.66.tgz", + "integrity": "sha1-3QNdQJ3zIqzIPf9ipgLxKleDu7M=" + } + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npm.taobao.org/through2/download/through2-2.0.5.tgz", + "integrity": "sha1-AcHjnrMdB8t9A6lqcIIyYLIxMs0=", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.npm.taobao.org/timers-browserify/download/timers-browserify-2.0.11.tgz", + "integrity": "sha1-gAsfPu4nLlvFPuRloE0OgEwxIR8=", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tinycolor2": { + "version": "1.4.2", + "resolved": "https://registry.npm.taobao.org/tinycolor2/download/tinycolor2-1.4.2.tgz", + "integrity": "sha1-P2pNEHGtB2dtf6Ry4frECnGdiAM=" + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/to-arraybuffer/download/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "http://registry.npm.taobao.org/to-object-path/download/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/to-readable-stream/download/to-readable-stream-1.0.0.tgz", + "integrity": "sha1-zgqgwvPfat+FLvtASng+d8BHV3E=", + "dev": true + }, + "to-regex": { + "version": "3.0.2", + "resolved": "http://registry.npm.taobao.org/to-regex/download/to-regex-3.0.2.tgz", + "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "http://registry.npm.taobao.org/to-regex-range/download/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "http://registry.npm.taobao.org/toggle-selection/download/toggle-selection-1.0.6.tgz", + "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" + }, + "trim-repeated": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/trim-repeated/download/trim-repeated-1.0.0.tgz", + "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, + "trough": { + "version": "1.0.5", + "resolved": "https://registry.npm.taobao.org/trough/download/trough-1.0.5.tgz", + "integrity": "sha1-uLY5zvrX0LsqvTfUM/+Ck++l9AY=" + }, + "truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/truncate-utf8-bytes/download/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=", + "requires": { + "utf8-byte-length": "^1.0.1" + } + }, + "ts-loader": { + "version": "8.0.4", + "resolved": "https://registry.npm.taobao.org/ts-loader/download/ts-loader-8.0.4.tgz?cache=0&sync_timestamp=1600495911045&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fts-loader%2Fdownload%2Fts-loader-8.0.4.tgz", + "integrity": "sha1-ArnJH7z9sxFNix6Yo4KSZScO7no=", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^1.0.2", + "micromatch": "^4.0.0", + "semver": "^6.0.0" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "http://registry.npm.taobao.org/braces/download/braces-3.0.2.tgz", + "integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "http://registry.npm.taobao.org/fill-range/download/fill-range-7.0.1.tgz", + "integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "http://registry.npm.taobao.org/is-number/download/is-number-7.0.0.tgz", + "integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npm.taobao.org/micromatch/download/micromatch-4.0.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmicromatch%2Fdownload%2Fmicromatch-4.0.2.tgz", + "integrity": "sha1-T8sJmb+fvC/L3SEvbWKbmlbDklk=", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npm.taobao.org/semver/download/semver-6.3.0.tgz", + "integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "http://registry.npm.taobao.org/to-regex-range/download/to-regex-range-5.0.1.tgz", + "integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "tslib": { + "version": "1.14.0", + "resolved": "https://registry.npm.taobao.org/tslib/download/tslib-1.14.0.tgz?cache=0&sync_timestamp=1601998793819&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftslib%2Fdownload%2Ftslib-1.14.0.tgz", + "integrity": "sha1-1iSYPz4sXgtVMHw91shqzXN2IsY=" + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "http://registry.npm.taobao.org/tty-browserify/download/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npm.taobao.org/tunnel/download/tunnel-0.0.6.tgz", + "integrity": "sha1-cvExSzSlsZLbASMk3yzFh8pH+Sw=", + "dev": true, + "optional": true + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npm.taobao.org/type-detect/download/type-detect-4.0.8.tgz", + "integrity": "sha1-dkb7XxiHHPu3dJ5pvTmmOI63RQw=" + }, + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npm.taobao.org/type-fest/download/type-fest-0.13.1.tgz?cache=0&sync_timestamp=1601425309553&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftype-fest%2Fdownload%2Ftype-fest-0.13.1.tgz", + "integrity": "sha1-AXLLW86AsL1ULqNI21DH4hg02TQ=", + "dev": true, + "optional": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "http://registry.npm.taobao.org/typedarray/download/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npm.taobao.org/typedarray-to-buffer/download/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha1-qX7nqf9CaRufeD/xvFES/j/KkIA=", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "4.0.3", + "resolved": "https://registry.npm.taobao.org/typescript/download/typescript-4.0.3.tgz?cache=0&sync_timestamp=1602227980758&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftypescript%2Fdownload%2Ftypescript-4.0.3.tgz", + "integrity": "sha1-FTu9Ro7wdyXB35x36LRT+NNqu6U=", + "dev": true + }, + "ua-parser-js": { + "version": "0.7.22", + "resolved": "https://registry.npm.taobao.org/ua-parser-js/download/ua-parser-js-0.7.22.tgz?cache=0&sync_timestamp=1599900430227&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fua-parser-js%2Fdownload%2Fua-parser-js-0.7.22.tgz", + "integrity": "sha1-lg32Cl+RHqjxyBjzdHuZxuF36uM=" + }, + "unified": { + "version": "9.2.0", + "resolved": "https://registry.npm.taobao.org/unified/download/unified-9.2.0.tgz?cache=0&sync_timestamp=1598034253769&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Funified%2Fdownload%2Funified-9.2.0.tgz", + "integrity": "sha1-Z6YsYnxAWJ7eu/YPU+39TYIgJ/g=", + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npm.taobao.org/is-buffer/download/is-buffer-2.0.4.tgz", + "integrity": "sha1-PlcvI8hBGlz9lVfISeNmXgspBiM=" + } + } + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/union-value/download/union-value-1.0.1.tgz", + "integrity": "sha1-C2/nuDWuzaYcbqTU8CwUIh4QmEc=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/uniq/download/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "http://registry.npm.taobao.org/unique-filename/download/unique-filename-1.1.1.tgz", + "integrity": "sha1-HWl2k2mtoFgxA6HmrodoG1ZXMjA=", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/unique-slug/download/unique-slug-2.0.2.tgz", + "integrity": "sha1-uqvOkQg/xk6UWw861hPiZPfNTmw=", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/unique-string/download/unique-string-2.0.0.tgz", + "integrity": "sha1-OcZFH4GvsnSd4rIz4/fF6IQ72J0=", + "dev": true, + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "unist-util-is": { + "version": "4.0.3", + "resolved": "https://registry.npm.taobao.org/unist-util-is/download/unist-util-is-4.0.3.tgz?cache=0&sync_timestamp=1604050918173&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Funist-util-is%2Fdownload%2Funist-util-is-4.0.3.tgz", + "integrity": "sha1-6LRNtV/CDEN1KzNGwRY0TUXXyR0=" + }, + "unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/unist-util-stringify-position/download/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha1-zOO/oc34W6c3XR1bF73Eytqb2do=", + "requires": { + "@types/unist": "^2.0.2" + } + }, + "unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/unist-util-visit/download/unist-util-visit-2.0.3.tgz?cache=0&sync_timestamp=1594459251384&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Funist-util-visit%2Fdownload%2Funist-util-visit-2.0.3.tgz", + "integrity": "sha1-w3A4kxRt9HIDu4qXla9H17lxIIw=", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + }, + "dependencies": { + "unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/unist-util-visit-parents/download/unist-util-visit-parents-3.1.1.tgz?cache=0&sync_timestamp=1603108261755&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Funist-util-visit-parents%2Fdownload%2Funist-util-visit-parents-3.1.1.tgz", + "integrity": "sha1-ZabOaY94prD1aqDojxOAGIbNrvY=", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + } + } + } + }, + "unist-util-visit-parents": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/unist-util-visit-parents/download/unist-util-visit-parents-1.1.2.tgz?cache=0&sync_timestamp=1603108261755&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Funist-util-visit-parents%2Fdownload%2Funist-util-visit-parents-1.1.2.tgz", + "integrity": "sha1-9uOv7ovb+WHA5vAo6jwEgAKMPQY=" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npm.taobao.org/universalify/download/universalify-0.1.2.tgz", + "integrity": "sha1-tkb2m+OULavOzJ1mOcgNwQXvqmY=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/unset-value/download/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "http://registry.npm.taobao.org/has-value/download/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/isobject/download/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "http://registry.npm.taobao.org/has-values/download/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/upath/download/upath-1.2.0.tgz?cache=0&sync_timestamp=1602008334498&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fupath%2Fdownload%2Fupath-1.2.0.tgz", + "integrity": "sha1-j2bbzVWog6za5ECK+LA1pQRMGJQ=", + "dev": true, + "optional": true + }, + "update-notifier": { + "version": "4.1.3", + "resolved": "https://registry.npm.taobao.org/update-notifier/download/update-notifier-4.1.3.tgz", + "integrity": "sha1-vobuE+jOSPtQBD/3IFe1vVmOHqM=", + "dev": true, + "requires": { + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.1", + "is-npm": "^4.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "pupa": "^2.0.1", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1601839226460&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz", + "integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-3.0.0.tgz", + "integrity": "sha1-P3PCv1JlkfV0zEksUeJFY0n4ROQ=", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz", + "integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz", + "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-4.0.0.tgz", + "integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz?cache=0&sync_timestamp=1598611771865&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-7.2.0.tgz", + "integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "uri-js": { + "version": "4.4.0", + "resolved": "https://registry.npm.taobao.org/uri-js/download/uri-js-4.4.0.tgz", + "integrity": "sha1-qnFCYd55PoqCNHp7zJznTobyhgI=", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npm.taobao.org/urix/download/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "http://registry.npm.taobao.org/url/download/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "http://registry.npm.taobao.org/punycode/download/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/url-parse-lax/download/url-parse-lax-3.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Furl-parse-lax%2Fdownload%2Furl-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "requires": { + "prepend-http": "^2.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "http://registry.npm.taobao.org/use/download/use-3.1.1.tgz", + "integrity": "sha1-1QyMrHmhn7wg8pEfVuuXP04QBw8=", + "dev": true + }, + "utf8-byte-length": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/utf8-byte-length/download/utf8-byte-length-1.0.4.tgz", + "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=" + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npm.taobao.org/util/download/util-0.11.1.tgz", + "integrity": "sha1-MjZzNyDsZLsn9uJvQhqqLhtYjWE=", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz?cache=0&sync_timestamp=1560975547815&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Finherits%2Fdownload%2Finherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "v8-compile-cache": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/v8-compile-cache/download/v8-compile-cache-2.1.1.tgz", + "integrity": "sha1-VLw83UMxe8qR413K8wWxpyN950U=", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "http://registry.npm.taobao.org/validate-npm-package-license/download/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha1-/JH2uce6FchX9MssXe/uw51PQQo=", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vfile": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/vfile/download/vfile-4.2.0.tgz?cache=0&sync_timestamp=1596111366966&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvfile%2Fdownload%2Fvfile-4.2.0.tgz", + "integrity": "sha1-JseKyS63CBawHUVl4AO35loqDgE=", + "requires": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "replace-ext": "1.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npm.taobao.org/is-buffer/download/is-buffer-2.0.4.tgz", + "integrity": "sha1-PlcvI8hBGlz9lVfISeNmXgspBiM=" + } + } + }, + "vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npm.taobao.org/vfile-message/download/vfile-message-2.0.4.tgz", + "integrity": "sha1-W0O4gXHUCerlhHfRPyPdQdUsNxo=", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/vm-browserify/download/vm-browserify-1.1.2.tgz?cache=0&sync_timestamp=1589682787766&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvm-browserify%2Fdownload%2Fvm-browserify-1.1.2.tgz", + "integrity": "sha1-eGQcSIuObKkadfUR56OzKobl3aA=", + "dev": true + }, + "warning": { + "version": "4.0.3", + "resolved": "http://registry.npm.taobao.org/warning/download/warning-4.0.3.tgz", + "integrity": "sha1-Fungd+uKhtavfWSqHgX9hbRnjKM=", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "watchpack": { + "version": "1.7.4", + "resolved": "https://registry.npm.taobao.org/watchpack/download/watchpack-1.7.4.tgz?cache=0&sync_timestamp=1600385568268&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwatchpack%2Fdownload%2Fwatchpack-1.7.4.tgz", + "integrity": "sha1-bp2lOzyAuy1lCBiPWyAEEIZs0ws=", + "dev": true, + "requires": { + "chokidar": "^3.4.1", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.0" + } + }, + "watchpack-chokidar2": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/watchpack-chokidar2/download/watchpack-chokidar2-2.0.0.tgz", + "integrity": "sha1-mUihhmy71suCTeoTp+1pH2yN3/A=", + "dev": true, + "optional": true, + "requires": { + "chokidar": "^2.1.8" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/anymatch/download/anymatch-2.0.0.tgz", + "integrity": "sha1-vLJLTzeTTZqnrBe0ra+J58du8us=", + "dev": true, + "optional": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "http://registry.npm.taobao.org/normalize-path/download/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "optional": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npm.taobao.org/binary-extensions/download/binary-extensions-1.13.1.tgz", + "integrity": "sha1-WYr+VHVbKGilMw0q/51Ou1Mgm2U=", + "dev": true, + "optional": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npm.taobao.org/chokidar/download/chokidar-2.1.8.tgz", + "integrity": "sha1-gEs6e2qZNYw8XGHnHYco8EHP+Rc=", + "dev": true, + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-1.2.13.tgz", + "integrity": "sha1-8yXLBFVZJCi88Rs4M3DvcOO/zDg=", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/glob-parent/download/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "http://registry.npm.taobao.org/is-glob/download/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/is-binary-path/download/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/readdirp/download/readdirp-2.2.1.tgz", + "integrity": "sha1-DodiKjMlqjPokihcr4tOhGUppSU=", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + } + } + }, + "webpack": { + "version": "4.44.2", + "resolved": "https://registry.npm.taobao.org/webpack/download/webpack-4.44.2.tgz", + "integrity": "sha1-a/4rCvBVyLLR6Q7SzZNj+EEma3I=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.3.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.7.4", + "webpack-sources": "^1.4.1" + } + }, + "webpack-cli": { + "version": "3.3.12", + "resolved": "https://registry.npm.taobao.org/webpack-cli/download/webpack-cli-3.3.12.tgz?cache=0&sync_timestamp=1602006448762&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwebpack-cli%2Fdownload%2Fwebpack-cli-3.3.12.tgz", + "integrity": "sha1-lOmtoIFFPNCqYJyZ5QABL9OtLUo=", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "cross-spawn": "^6.0.5", + "enhanced-resolve": "^4.1.1", + "findup-sync": "^3.0.0", + "global-modules": "^2.0.0", + "import-local": "^2.0.0", + "interpret": "^1.4.0", + "loader-utils": "^1.4.0", + "supports-color": "^6.1.0", + "v8-compile-cache": "^2.1.1", + "yargs": "^13.3.2" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-6.1.0.tgz?cache=0&sync_timestamp=1598611709087&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-6.1.0.tgz", + "integrity": "sha1-B2Srxpxj1ayELdSGfo0CXogN+PM=", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npm.taobao.org/webpack-sources/download/webpack-sources-1.4.3.tgz?cache=0&sync_timestamp=1601308261594&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwebpack-sources%2Fdownload%2Fwebpack-sources-1.4.3.tgz", + "integrity": "sha1-7t2OwLko+/HL/plOItLYkPMwqTM=", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "http://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "dev": true + } + } + }, + "whatwg-fetch": { + "version": "3.5.0", + "resolved": "https://registry.npm.taobao.org/whatwg-fetch/download/whatwg-fetch-3.5.0.tgz", + "integrity": "sha1-YFos0KcUbl2xQeKdHGKrhMDEyGg=" + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "http://registry.npm.taobao.org/whatwg-mimetype/download/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha1-PUseAxLSB5h5+Cav8Y2+7KWWD78=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npm.taobao.org/which/download/which-1.3.1.tgz?cache=0&sync_timestamp=1589682812246&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwhich%2Fdownload%2Fwhich-1.3.1.tgz", + "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "http://registry.npm.taobao.org/which-module/download/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/widest-line/download/widest-line-3.1.0.tgz", + "integrity": "sha1-gpIzO79my0X/DeFgOxNreuFJbso=", + "dev": true, + "requires": { + "string-width": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npm.taobao.org/ansi-regex/download/ansi-regex-5.0.0.tgz", + "integrity": "sha1-OIU59VF5vzkznIGvMKZU1p+Hy3U=", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npm.taobao.org/emoji-regex/download/emoji-regex-8.0.0.tgz", + "integrity": "sha1-6Bj9ac5cz8tARZT4QpY79TFkzDc=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha1-8Rb4Bk/pCz94RKOJl8C3UFEmnx0=", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/string-width/download/string-width-4.2.0.tgz", + "integrity": "sha1-lSGCxGzHssMT0VluYjmSvRY7crU=", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-6.0.0.tgz?cache=0&sync_timestamp=1573280518303&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-6.0.0.tgz", + "integrity": "sha1-CxVx3XZpzNTz4G4U7x7tJiJa5TI=", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npm.taobao.org/worker-farm/download/worker-farm-1.7.0.tgz", + "integrity": "sha1-JqlMU5G7ypJhUgAvabhKS/dy5ag=", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npm.taobao.org/wrap-ansi/download/wrap-ansi-5.1.0.tgz", + "integrity": "sha1-H9H2cjXVttD+54EFYAG/tpTAOwk=", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/wrappy/download/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npm.taobao.org/write-file-atomic/download/write-file-atomic-3.0.3.tgz", + "integrity": "sha1-Vr1cWlxwSBzRnFcb05q5ZaXeVug=", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/xdg-basedir/download/xdg-basedir-4.0.0.tgz", + "integrity": "sha1-S8jZmEQDaWIl74OhVzy7y0552xM=", + "dev": true + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npm.taobao.org/xmlbuilder/download/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "dev": true + }, + "xmldom": { + "version": "0.1.31", + "resolved": "https://registry.npm.taobao.org/xmldom/download/xmldom-0.1.31.tgz", + "integrity": "sha1-t2yaG9nwqXN+WnLcNyMc84N14v8=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npm.taobao.org/xtend/download/xtend-4.0.2.tgz", + "integrity": "sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q=" + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/y18n/download/y18n-4.0.0.tgz", + "integrity": "sha1-le+U+F7MgdAHwmThkKEg8KPIVms=", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/yallist/download/yallist-3.1.1.tgz", + "integrity": "sha1-27fa+b/YusmrRev2ArjLrQ1dCP0=", + "dev": true + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npm.taobao.org/yargs/download/yargs-13.3.2.tgz?cache=0&sync_timestamp=1600660037884&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fyargs%2Fdownload%2Fyargs-13.3.2.tgz", + "integrity": "sha1-rX/+/sGqWVZayRX4Lcyzipwxot0=", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npm.taobao.org/yargs-parser/download/yargs-parser-13.1.2.tgz", + "integrity": "sha1-Ew8JcC667vJlDVTObj5XBvek+zg=", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "http://registry.npm.taobao.org/yauzl/download/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/tools/behavior3editor/package.json b/tools/behavior3editor/package.json new file mode 100644 index 0000000..815a3ef --- /dev/null +++ b/tools/behavior3editor/package.json @@ -0,0 +1,65 @@ +{ + "name": "behavior3editor", + "version": "0.1.8", + "description": "行为树编辑器", + "main": "./dist/main.js", + "scripts": { + "prestart": "webpack --config webpack.config.js", + "start": "electron .", + "build": "electron-packager ./ behavior3editor --platform=win32 --arch=x64 --out=./build --overwrite", + "dist": "npm run prestart && electron-builder --dir && electron-builder --win --x64" + }, + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/zhandouxiaojiji/behavior3editor.git" + }, + "author": "zhandouxiaojiji", + "license": "MIT", + "bugs": { + "url": "https://github.com/zhandouxiaojiji/behavior3editor/issues" + }, + "build": { + "appId": "com.example.app", + "directories": { + "output": "build" + } + }, + "homepage": "https://github.com/zhandouxiaojiji/behavior3editor#readme", + "devDependencies": { + "@types/electron-localshortcut": "^3.1.0", + "@types/node": "^14.14.8", + "@types/rimraf": "^3.0.0", + "copy-webpack-plugin": "^6.3.1", + "css-loader": "^4.3.0", + "electron": "^10.1.3", + "electron-builder": "^22.9.1", + "electron-localshortcut": "^3.2.1", + "electron-packager": "^15.1.0", + "file-loader": "^6.1.1", + "less": "^3.12.2", + "less-loader": "^7.1.0", + "node-fetch": "^2.6.1", + "source-map-loader": "^1.1.0", + "style-loader": "^2.0.0", + "ts-loader": "^8.0.4", + "typescript": "^4.0.3", + "webpack": "^4.44.2", + "webpack-cli": "^3.3.12" + }, + "dependencies": { + "@ant-design/icons": "^4.3.0", + "@antv/g6": "^3.8.1", + "@types/react": "^16.9.51", + "@types/react-dom": "^16.9.8", + "antd": "^4.6.6", + "lodash": "^4.17.20", + "react": "^16.13.1", + "react-confirm-alert": "^2.6.2", + "react-dom": "^16.13.1", + "react-markdown": "^5.0.2", + "rimraf": "^3.0.2", + "sanitize-filename": "^1.6.3", + "shineout": "^1.6.1", + "sync-request": "^6.1.0" + } +} diff --git a/tools/behavior3editor/readme/preview.png b/tools/behavior3editor/readme/preview.png new file mode 100644 index 0000000..c9cf217 Binary files /dev/null and b/tools/behavior3editor/readme/preview.png differ diff --git a/tools/behavior3editor/render-process/Editor/Editor.css b/tools/behavior3editor/render-process/Editor/Editor.css new file mode 100644 index 0000000..53434a6 --- /dev/null +++ b/tools/behavior3editor/render-process/Editor/Editor.css @@ -0,0 +1,28 @@ +.editor { + width : 100%; + height : 100%; + overflow: hidden; +} + +.editorBd { + width : 100%; + height : 100%; + display: flex; + +} + +.editorContent { + /* min-width: 500px; */ + height : 100%; +} + +.editorSidebar { + /* min-width: 500px; */ + height : 100%; + overflow-y: auto; +} + +.mind { + width : 100%; + height: 800px; +} \ No newline at end of file diff --git a/tools/behavior3editor/render-process/Editor/NodePanel.tsx b/tools/behavior3editor/render-process/Editor/NodePanel.tsx new file mode 100644 index 0000000..9820c03 --- /dev/null +++ b/tools/behavior3editor/render-process/Editor/NodePanel.tsx @@ -0,0 +1,305 @@ +import React from "react"; +import { + Card, + Divider, + Form, + Input, + AutoComplete, + Select, + Switch, + InputNumber, + notification, + message, +} from "antd"; +import { INode } from "@antv/g6/lib/interface/item"; +import { + BehaviorNodeModel, + BehaviorNodeTypeModel, + ArgsDefType, +} from "../../common/BehaviorTreeModel"; +import Settings from "../../main-process/Settings"; +import { FormInstance } from "antd/lib/form"; +import Markdown from "react-markdown"; + +const { Item } = Form; +const { Option } = Select; + +interface NodePanelProps { + model: BehaviorNodeModel; + settings: Settings; + updateNode: (id: string, forceUpdate: boolean) => void; + pushUndoStack: () => void; +} + +interface NodePanelState {} + +export default class NodePanel extends React.Component { + formRef = React.createRef(); + + componentDidUpdate() { + this.formRef.current.resetFields(); + this.formRef.current.setFieldsValue(this.getInitialValues()); + } + + getInitialValues() { + const { model } = this.props; + const initialValues: any = { + name: model.name, + desc: model.desc, + debug: model.debug, + customArgs: model.args ? JSON.stringify(model.args, null, " ") : "", + }; + if (model.args) { + for (let k in model.args) { + initialValues[`args.${k}`] = model.args[k]; + } + } + if (model.input) { + model.input.forEach((v, i) => { + initialValues[`input.${i}`] = v; + }); + } + if (model.output) { + model.output.forEach((v, i) => { + initialValues[`output.${i}`] = v; + }); + } + return initialValues; + } + + onFinish = (values: any) => { + console.log("Success:", values); + const { updateNode, pushUndoStack, model, settings } = this.props; + const conf = settings.getNodeConf(values.name); + if (!conf) { + notification.warn({ message: `节点${values.name}未定义` }); + return; + } + var args: any = {}; + if (values.customArgs) { + try { + args = JSON.parse(values.customArgs); + } catch (e) { + message.warn(`您输入的自定义参数不符合json格式${values.customArgs}`); + return; + } + } + + pushUndoStack(); + + var forceUpdate = false; + if (model.name != values.name) { + model.name = values.name; + forceUpdate = true; + } + model.desc = values.desc; + model.debug = values.debug; + + if (conf.args) { + conf.args.forEach((e) => { + const k = "args." + e.name; + if (e.type.indexOf("number") > 0) { + args[e.name] = Number(values[k]); + } else { + args[e.name] = values[k]; + } + }); + } + + model.args = args; + this.formRef.current.setFieldsValue({ + customArgs: model.args ? JSON.stringify(model.args, null, " ") : "", + }); + + if (conf.input) { + model.input = []; + conf.input.forEach((e, i) => { + model.input.push(values["input." + i] || ""); + }); + } else { + model.input = null; + } + + if (conf.output) { + model.output = []; + conf.output.forEach((e, i) => { + model.output.push(values["output." + i] || ""); + }); + } else { + model.output = null; + } + + if (forceUpdate) { + this.forceUpdate(); + } + updateNode(model.id.toString(), forceUpdate); + }; + + onFinishFailed = (errorInfo: any) => { + console.log("Failed:", errorInfo); + }; + + handleSubmit = () => { + console.log("handleSubmit"); + this.formRef.current.submit(); + }; + + render() { + const { model, settings } = this.props; + const conf = settings.getNodeConf(model.name); + const title = conf.desc; + + const options: any = []; + settings.nodeConfig.map((e) => { + options.push({ label: `${e.name}(${e.desc})`, value: e.name }); + }); + + const layout = { + labelCol: { span: 6 }, + wrapperCol: { span: 18 }, + }; + return ( + +
+ + + + + { + return ( + option.label.toUpperCase().indexOf(inputValue.toUpperCase()) !== + -1 + ); + }} + /> + + + + + + + + + {this.renderInputs(conf)} + {this.renderArgs(conf)} + {this.renderOutputs(conf)} + +
+ ); + } + + renderArgs(conf: BehaviorNodeTypeModel) { + if (!conf || !conf.args || conf.args.length == 0) { + return null; + } + + // 普通参数 + const normalArgs = (e: ArgsDefType) => { + const required = e.type.indexOf("?") == -1; + if (e.type.indexOf("string") >= 0) { + return ; + } else if (e.type.indexOf("int") >= 0) { + return ; + } else if (e.type.indexOf("boolean") >= 0) { + return ; + } else if (e.type.indexOf("lua") >= 0) { + return ; + } else if (e.type.indexOf("enum") >= 0 ) { + return ; + } + }; + + // 自定义参数 + const customArgs = () => { + return ( + + + + ); + }; + + return ( +
+ +

常量参数

+
+ {conf && + conf.args && + conf.args.map((e, i: number) => { + const required = e.type.indexOf("?") == -1; + return ( + = 0 ? "checked" : undefined + } + rules={[{ required, message: `${e.desc}(${e.name})为必填字段` }]} + > + {normalArgs(e)} + + ); + })} + {customArgs()} +
+ ); + } + + renderInputs(conf: BehaviorNodeTypeModel) { + if (!conf.input || !conf.input || conf.input.length == 0) { + return null; + } + + return ( +
+ +

输入变量

+
+ {conf.input.map((e, i) => { + return ( + + + + ); + })} +
+ ); + } + + renderOutputs(conf: BehaviorNodeTypeModel) { + if (!conf.output || !conf.output || conf.output.length == 0) { + return null; + } + return ( +
+ +

输出变量

+
+ {conf.output.map((e, i) => { + return ( + + + + ); + })} +
+ ); + } +} diff --git a/tools/behavior3editor/render-process/Editor/TreePanel.tsx b/tools/behavior3editor/render-process/Editor/TreePanel.tsx new file mode 100644 index 0000000..6120d2f --- /dev/null +++ b/tools/behavior3editor/render-process/Editor/TreePanel.tsx @@ -0,0 +1,89 @@ +import React from "react"; +import { Button, Card, Form, Input } from "antd"; +import { BehaviorTreeModel } from "../../common/BehaviorTreeModel"; +import { FormInstance } from "antd/lib/form"; + +const { Item } = Form; + +interface TreePanelProps { + model?: BehaviorTreeModel; + onRenameTree: (name: string) => void; + onRemoveTree: () => void; + onChangeTreeDesc: (desc: string) => void; +} + +interface TreePanelState { +} + +const inlineFormItemLayout = { + labelCol: { + sm: { span: 4 }, + }, + wrapperCol: { + sm: { span: 12 }, + }, +}; + +export default class TreePanel extends React.Component { + name: string = ''; + desc: string = ''; + formRef = React.createRef(); + + componentDidMount() { + const { model } = this.props; + this.name = model.name; + this.desc = model.desc; + this.formRef.current.resetFields(); + this.formRef.current.setFieldsValue({ + name: model.name, + desc: model.desc, + }) + } + + render() { + const { model } = this.props; + if (!model) { + return
; + } + return ( + +
+ + + + + + + {/* + + */} + +
+ ); + } + + handleSubmit = () => { + const { model, onRenameTree, onChangeTreeDesc } = this.props; + const name = this.formRef.current.getFieldValue("name"); + const desc = this.formRef.current.getFieldValue("desc"); + if (name != this.name) { + console.log("change name", name); + this.name = name; + model.name = name; + onRenameTree(name); + } + if (desc != this.desc) { + console.log("change desc", desc); + this.desc = desc; + model.desc = desc; + onChangeTreeDesc(desc); + } + } + + handleRemoveTree = () => { + this.props.onRemoveTree(); + } +} diff --git a/tools/behavior3editor/render-process/Editor/index.tsx b/tools/behavior3editor/render-process/Editor/index.tsx new file mode 100644 index 0000000..a862d16 --- /dev/null +++ b/tools/behavior3editor/render-process/Editor/index.tsx @@ -0,0 +1,511 @@ +import * as React from "react"; +import * as fs from "fs"; +import * as path from "path"; +import { Row, Col, message, Card } from "antd"; +import NodePanel from "./NodePanel"; +import G6, { TreeGraph } from "@antv/g6"; +import { G6GraphEvent } from "@antv/g6/lib/interface/behavior"; +import * as Utils from "../../common/Utils"; +import { + BehaviorTreeModel, + GraphNodeModel, + BehaviorNodeModel, +} from "../../common/BehaviorTreeModel"; +import TreePanel from "./TreePanel"; +import Settings from "../../main-process/Settings"; + +import "./Editor.css"; +import { clipboard } from "electron"; + +export interface EditorProps { + filepath: string; + onChangeSaveState: (unsave: boolean) => void; +} + +interface EditorState { + curNodeId?: string; +} + +export default class Editor extends React.Component { + private ref: React.RefObject; + state: EditorState = {}; + + private graph: TreeGraph; + private dragSrcId: string; + private dragDstId: string; + private autoId: number; + private undoStack: BehaviorNodeModel[] = []; + private redoStack: BehaviorNodeModel[] = []; + private treeModel: BehaviorTreeModel; + private settings: Settings; + private data: GraphNodeModel; + private unsave: boolean = false; + + constructor(props: EditorProps) { + super(props); + this.ref = React.createRef(); + + this.settings = Utils.getRemoteSettings(); + const str = fs.readFileSync(this.props.filepath, "utf8"); + this.treeModel = JSON.parse(str); + this.data = Utils.createTreeData(this.treeModel.root, this.settings); + this.autoId = Utils.refreshNodeId(this.data); + } + + shouldComponentUpdate(nextProps: EditorProps, nextState: EditorState) { + return this.props.filepath != nextProps.filepath || this.state.curNodeId != nextState.curNodeId; + } + + componentDidMount() { + const graph = new TreeGraph({ + container: this.ref.current, + width: window.screen.width * 0.66, + height: window.screen.height, + animate: false, + maxZoom: 2, + // fitCenter: true, + modes: { + default: [ + "drag-canvas", + "zoom-canvas", + "click-select", + "hover", + { + type: "collapse-expand", + trigger: "dblclick", + onChange: (item, collapsed) => { + this.onSelectNode(item.getID()); + const data = item.getModel(); + data.collapsed = collapsed; + graph.setItemState(item, "collapsed", data.collapsed as boolean); + const icon = data.collapsed ? G6.Marker.expand : G6.Marker.collapse; + const marker = item + .get("group") + .find((ele: any) => ele.get("name") === "collapse-icon"); + marker.attr("symbol", icon); + return true; + }, + }, + ], + }, + defaultEdge: { + type: "cubic-horizontal", + style: { + stroke: "#A3B1BF", + }, + }, + defaultNode: { + type: "TreeNode", + }, + layout: { + type: "compactBox", + direction: "LR", + getHGap: () => 50, + getWidth: (d: GraphNodeModel) => { + return 150; + }, + getHeight: (d: GraphNodeModel) => { + if (d.size) { + return d.size[1]; + } else { + return 50; + } + }, + }, + }); + + graph.on("node:mouseenter", (e: G6GraphEvent) => { + const { item } = e; + if (item.hasState("selected")) { + return; + } + graph.setItemState(item, "hover", true); + }); + + graph.on("node:mouseleave", (e: G6GraphEvent) => { + const { item } = e; + if (item.hasState("selected")) { + return; + } + graph.setItemState(item, "hover", false); + }); + + graph.on("nodeselectchange", (e: G6GraphEvent) => { + if (e.target) { + this.onSelectNode(e.target.getID()); + } else { + this.onSelectNode(null); + } + }); + + const clearDragDstState = () => { + if (this.dragDstId) { + graph.setItemState(this.dragDstId, "dragRight", false); + graph.setItemState(this.dragDstId, "dragDown", false); + graph.setItemState(this.dragDstId, "dragUp", false); + this.dragDstId = null; + } + }; + + const clearDragSrcState = () => { + if (this.dragSrcId) { + graph.setItemState(this.dragSrcId, "dragSrc", false); + this.dragSrcId = null; + } + }; + + graph.on("node:dragstart", (e: G6GraphEvent) => { + this.dragSrcId = e.item.getID(); + graph.setItemState(this.dragSrcId, "dragSrc", true); + }); + graph.on("node:dragend", (e: G6GraphEvent) => { + if (this.dragSrcId) { + graph.setItemState(this.dragSrcId, "dragSrc", false); + this.dragSrcId = null; + } + }); + + graph.on("node:dragover", (e: G6GraphEvent) => { + const dstNodeId = e.item.getID(); + if (dstNodeId == this.dragSrcId) { + return; + } + + if (this.dragDstId) { + graph.setItemState(this.dragDstId, "dragRight", false); + graph.setItemState(this.dragDstId, "dragDown", false); + graph.setItemState(this.dragDstId, "dragUp", false); + } + + const box = e.item.getBBox(); + if (e.x > box.minX + box.width * 0.6) { + graph.setItemState(dstNodeId, "dragRight", true); + } else if (e.y > box.minY + box.height * 0.5) { + graph.setItemState(dstNodeId, "dragDown", true); + } else { + graph.setItemState(dstNodeId, "dragUp", true); + } + this.dragDstId = dstNodeId; + }); + + graph.on("node:dragleave", (e: G6GraphEvent) => { + clearDragDstState(); + }); + + graph.on("node:drop", (e: G6GraphEvent) => { + const srcNodeId = this.dragSrcId; + const dstNode = e.item; + + var dragDir; + if (dstNode.hasState("dragRight")) { + dragDir = "dragRight"; + } else if (dstNode.hasState("dragDown")) { + dragDir = "dragDown"; + } else if (dstNode.hasState("dragUp")) { + dragDir = "dragUp"; + } + + clearDragSrcState(); + clearDragDstState(); + + if (!srcNodeId) { + console.log("no drag src"); + return; + } + + if (srcNodeId == dstNode.getID()) { + console.log("drop same node"); + return; + } + + const rootData = graph.findDataById("1"); + const srcData = graph.findDataById(srcNodeId); + const srcParent = Utils.findParent(rootData, srcNodeId); + const dstData = graph.findDataById(dstNode.getID()); + const dstParent = Utils.findParent(rootData, dstNode.getID()); + if (!srcParent) { + console.log("no parent!"); + return; + } + + if (Utils.findFromAllChildren(srcData, dstData.id)) { + // 不能将父节点拖到自已的子孙节点 + console.log("cannot move to child"); + return; + } + + const removeSrc = () => { + this.pushUndoStack(); + srcParent.children = srcParent.children.filter((e) => e.id != srcData.id); + }; + console.log("dstNode", dstNode); + if (dragDir == "dragRight") { + removeSrc(); + if (!dstData.children) { + dstData.children = []; + } + dstData.children.push(srcData); + } else if (dragDir == "dragUp") { + if (!dstParent) { + return; + } + removeSrc(); + const idx = dstParent.children.findIndex((e) => e.id == dstData.id); + dstParent.children.splice(idx, 0, srcData); + } else if (dragDir == "dragDown") { + if (!dstParent) { + return; + } + removeSrc(); + const idx = dstParent.children.findIndex((e) => e.id == dstData.id); + dstParent.children.splice(idx + 1, 0, srcData); + } else { + return; + } + + // console.log("cur data", graph.findDataById('1')); + this.changeWithoutAnim(); + }); + + graph.data(this.data); + graph.render(); + graph.fitCenter(); + graph.set("animate", true); + + this.graph = graph; + + this.forceUpdate(); + } + + onSelectNode(curNodeId: string | null) { + const graph = this.graph; + + if (this.state.curNodeId) { + graph.setItemState(this.state.curNodeId, "selected", false); + } + + this.setState({ curNodeId }); + if (this.state.curNodeId) { + graph.setItemState(this.state.curNodeId, "selected", true); + } + } + + createNode(name: string) { + console.log("editor create node", name); + const { curNodeId } = this.state; + if (!curNodeId) { + message.warn("未选中节点"); + return; + } + this.pushUndoStack(); + const curNodeData = this.graph.findDataById(curNodeId); + const newNodeData: BehaviorNodeModel = { + id: this.autoId++, + name: name, + }; + if (!curNodeData.children) { + curNodeData.children = []; + } + curNodeData.children.push(Utils.createTreeData(newNodeData, this.settings)); + this.changeWithoutAnim(); + } + + deleteNode() { + console.log("editor delete node"); + const { curNodeId } = this.state; + if (!curNodeId) { + return; + } + + if (curNodeId == "1") { + message.warn("根节点不能删除!"); + return; + } + + this.onSelectNode(null); + this.pushUndoStack(); + const rootData = this.graph.findDataById("1"); + const parentData = Utils.findParent(rootData, curNodeId); + parentData.children = parentData.children.filter((e) => e.id != curNodeId); + this.changeWithoutAnim(); + } + + changeWithoutAnim() { + this.graph.set("animate", false); + this.graph.changeData(); + this.graph.layout(); + this.graph.set("animate", true); + + this.props.onChangeSaveState(true); + this.unsave = true; + } + + save() { + if (!this.unsave) { + return; + } + const { filepath } = this.props; + const data = this.graph.findDataById("1") as GraphNodeModel; + this.autoId = Utils.refreshNodeId(data); + const root = Utils.createFileData(data); + const treeModel = { + name: path.basename(filepath).slice(0, -5), + root, + desc: this.treeModel.desc, + } as BehaviorTreeModel; + fs.writeFileSync( + filepath, + JSON.stringify(treeModel, null, 2) + ); + this.props.onChangeSaveState(false); + this.unsave = false; + + this.graph.set("animate", false); + this.graph.changeData(Utils.createTreeData(root, this.settings)); + this.graph.layout(); + this.graph.fitCenter(); + this.graph.set("animate", true); + + return treeModel; + } + + copyNode() { + console.log("editor copy node"); + const { curNodeId } = this.state; + if (!curNodeId) { + return; + } + const data = this.graph.findDataById(curNodeId) as GraphNodeModel; + clipboard.writeText(JSON.stringify(Utils.cloneNodeData(data), null, 2)); + } + + pasteNode() { + const { curNodeId } = this.state; + if (!curNodeId) { + message.warn("未选中节点"); + return; + } + const curNodeData = this.graph.findDataById(curNodeId); + try { + const str = clipboard.readText(); + if (!str || str == "") { + return; + } + this.pushUndoStack(); + const data = Utils.createTreeData(JSON.parse(str), this.settings); + this.autoId = Utils.refreshNodeId(data, this.autoId); + this.onSelectNode(null); + if (!curNodeData.children) { + curNodeData.children = []; + } + curNodeData.children.push(data); + // this.autoId = Utils.refreshNodeId(this.graph.findDataById("1") as GraphNodeModel); + this.changeWithoutAnim(); + } catch (error) { + // message.error("粘贴数据有误"); + console.log("paste error"); + } + } + + useStackData(data: BehaviorNodeModel) { + this.graph.set("animate", false); + this.graph.changeData(Utils.createTreeData(data, this.settings)); + this.graph.layout(); + this.graph.fitCenter(); + this.graph.set("animate", true); + + this.props.onChangeSaveState(true); + this.unsave = true; + } + + pushUndoStack(keepRedo?: boolean) { + this.undoStack.push(Utils.cloneNodeData(this.graph.findDataById('1') as GraphNodeModel)); + console.log("push undo", this.undoStack); + if (!keepRedo) { + this.redoStack = []; + } + } + + pushRedoStack() { + this.redoStack.push(Utils.cloneNodeData(this.graph.findDataById('1') as GraphNodeModel)); + console.log("push redo", this.redoStack); + } + + undo() { + if (this.undoStack.length == 0) { + return; + } + const data = this.undoStack.pop(); + this.pushRedoStack(); + this.useStackData(data); + } + + redo() { + if (this.redoStack.length == 0) { + return; + } + const data = this.redoStack.pop(); + this.pushUndoStack(true); + this.useStackData(data); + } + + changeTreeDesc(desc: string) { + this.treeModel.desc = desc; + this.settings.setTreeDesc(this.props.filepath, desc); + this.unsave = true; + this.save(); + } + + render() { + const { curNodeId } = this.state; + console.log("render tree", curNodeId); + var curNode: any; + if (curNodeId) { + curNode = this.graph.findDataById(curNodeId); + } + + return ( +
+ +
+ + {curNode ? ( + { + if (forceUpdate) { + const data: any = this.graph.findDataById(id); + data.conf = this.settings.getNodeConf(data.name); + data.size = Utils.calcTreeNodeSize(data); + this.changeWithoutAnim(); + } + const item = this.graph.findById(id); + item.draw(); + this.props.onChangeSaveState(true); + this.unsave = true; + }} + pushUndoStack={() => { + this.pushUndoStack(); + }} + /> + ) : ( + { + + }} + onRemoveTree={() => { + + }} + onChangeTreeDesc={(desc) => { + this.changeTreeDesc(desc); + }} + /> + )} + + + + ); + } +} diff --git a/tools/behavior3editor/render-process/Explorer.tsx b/tools/behavior3editor/render-process/Explorer.tsx new file mode 100644 index 0000000..aa52f48 --- /dev/null +++ b/tools/behavior3editor/render-process/Explorer.tsx @@ -0,0 +1,430 @@ +import React, { ChangeEvent, Component } from "react"; +import { Input } from "antd"; +import { Tree } from "shineout"; +import { FolderOutlined, FileOutlined, FolderOpenOutlined } from "@ant-design/icons"; +import * as fs from "fs"; +import * as path from "path"; +import { ipcRenderer } from "electron"; +import MainEventType from "../common/MainEventType"; +import Settings from "../main-process/Settings"; +import * as Utils from "../common/Utils"; + +const { Search } = Input; +const DirectoryTree = Tree; + + +class FileDataNode { + name: string; //display name + desc: string; //describe + filepath: string; //file full path + isFolder: boolean; + + settings: Settings; + + parent: FileDataNode; + visible: boolean = true; + children: FileDataNode[]; + + public constructor(init?: Partial, settings?: Settings) { + Object.assign(this, init); + this.settings = settings; + } + + get id() { + return this.filepath; + } + + get text() { + if (!this.desc) { + return this.name; + } + return `${this.name}(${this.desc})`; + } + + get path() { + return this.filepath; + } + + public getRenderData() { + if (!this.visible) { + return null; + } + let ret = new FileDataNode({ + name: this.name, + desc: this.isFolder ? undefined : this.settings.getTreeDesc(this.filepath), + filepath: this.filepath, + isFolder: this.isFolder, + children: new Array(), + }, this.settings); + + for (const child of this.children) { + const data = child.getRenderData(); + if (data) { + ret.children.push(data); + } + } + return ret; + } + + loadChilds(recursive?: boolean) { + const folder = this.path; + if (folder == "" || !fs.existsSync(folder)) { + return []; + } + const files = fs.readdirSync(folder); + const list: FileDataNode[] = []; + files.forEach((filename) => { + const fullPath = path.join(folder, filename); + const stat = fs.statSync(fullPath); + const isFolder = stat.isDirectory(); + const name = isFolder ? filename : filename.slice(0, -5); + const node = new FileDataNode({ + name: name, + desc: isFolder ? undefined : this.settings.getTreeDesc(fullPath), + filepath: fullPath, + isFolder: isFolder, + parent: this, + }, this.settings); + + (node.children = + isFolder && recursive ? (node.loadChilds(recursive) as FileDataNode[]) : []), + list.push(node); + }); + list.sort((a, b) => { + const av = a.isFolder ? 100 : 0; + const bv = b.isFolder ? 100 : 0; + return bv - av; + }); + return list; + } + + addChild(filePath: string) { + const isDir = fs.statSync(filePath).isDirectory(); + const newNode = new FileDataNode({ + name: path.basename(filePath), + filepath: filePath, + isFolder: isDir, + parent: this, + children: [], + }, this.settings); + this.children = [newNode, ...this.children]; + } + + removeFromParent() { + const parent = this.parent; + if (parent) { + const index = parent.children.indexOf(this); + parent.children.splice(index, 1); + } + } + + expandSelf(recursive: boolean = true) { + if (this.isFolder) { + this.children = this.loadChilds(recursive); + } + } + + findChild(id: string): FileDataNode { + for (const child of this.children) { + if (child.id == id) { + return child; + } else { + const ret = child.findChild(id); + if (ret) { + return ret; + } + } + } + return null; + } + + getList(): FileDataNode[] { + let ret = new Array(this); + for (const child of this.children) { + const childList = child.getList(); + ret.push(...childList); + } + return ret; + } + + setVisible(keyWord: string) { + let ret = false; + for (let i = this.children.length - 1; i >= 0; i--) { + const child = this.children[i]; + if (!child.isFolder) { + const keyStr = child.id; + child.visible = keyStr.includes(keyWord); + } else { + child.visible = child.setVisible(keyWord); + } + ret = child.visible || ret; + } + return ret; + } + + // name:string; + // path: string; + // isFolder : boolean; + // isEditing?: boolean; + // children?: NodeData[]; +} + +const NodeActions: { [x: string]: string } = { + ["create"]: "New Tree", + ["createFolder"]: "New Folder", + ["rename"]: "Rename", + ["delete"]: "Delete", + ["reveal_in_explorer"]: "Reveal In File Explorer", +}; + +interface ExplorerNodeProps { + visible: boolean; + title: string; + selected: boolean; + expended: boolean; + isLeaf: boolean; + searchKey: string; +} +class ExplorerNode extends Component { + shouldComponentUpdate(nextProps: ExplorerNodeProps) { + return JSON.stringify(this.props) != JSON.stringify(nextProps); + } + + render() { + const { title, visible, selected, expended, isLeaf, searchKey } = this.props; + if (!visible) { + return null; + } + + const index = title.indexOf(searchKey); + const beforeStr = title.substr(0, index); + const afterStr = title.substr(index + searchKey.length); + + return ( +
+ {!isLeaf ? (expended ? : ) : } + {isLeaf && index > -1 ? ( + + {beforeStr} + {searchKey} + {afterStr} + + ) : ( + {title} + )} +
+ ); + } +} + +export interface ExplorerProps { + workdir: string; + onOpenTree: (path: string) => void; + onDeleteTree: (path: string) => void; +} + + +interface ExplorerState { + root: FileDataNode; + searchKey: string; + selectedKey: string; + defaultExpandedKeys: string[]; + expandedKeys: string[]; + autoExpandParent: boolean; + rightClickNode: { + pageX: number; + pageY: number; + key: string; + }; +} + +export default class Explorer extends Component { + state: ExplorerState = { + root: this.getRootNode(this.props.workdir), + searchKey: "", + selectedKey: "", + rightClickNode: null, + expandedKeys: [], + defaultExpandedKeys: [], + autoExpandParent: true, + }; + + curWorkdir: string = ""; + settings: Settings; + + shouldComponentUpdate(nextProps: ExplorerProps) { + const shouldUpdate = this.curWorkdir != nextProps.workdir; + this.curWorkdir = nextProps.workdir; + return shouldUpdate; + } + + componentDidMount() { + this.settings = Utils.getRemoteSettings(); + ipcRenderer.on(MainEventType.CREATE_TREE, (event: any, path: string) => { + console.log("on Create tree", path); + this.props.onOpenTree(path); + this.state.root.expandSelf(); + this.forceUpdate(); + }); + + ipcRenderer.on(MainEventType.OPEN_DIR, (event: any, workdir: any, workspace: string) => { + console.log("prop on open workspace", workspace); + this.updateRoot(); + this.forceUpdate(); + }); + + this.updateRoot(); + } + + updateRoot() { + var workdir = this.props.workdir; + if (!workdir || workdir === "") { + return; + } + const root = this.getRootNode(workdir); + root.expandSelf() + this.setState({ + root: root, + expandedKeys: [root.id], + defaultExpandedKeys: [root.id], + }); + + this.forceUpdate(); + } + + getRootNode(workdir: string) { + if (workdir && workdir !== "") { + return new FileDataNode({ + name: path.basename(workdir), + filepath: workdir, + isFolder: true, + parent: null, + children: [] + }, this.settings); + } else { + return null; + } + } + + // renderContextMeun(selectedNode: FileDataNode) { + // const actions = Object.keys(NodeActions); + // return ( + // + // {actions.map((action) => { + // const displayName: string = NodeActions[action]; + // return ( + // { + // //this.handleContextMenu({ node: selectedNode, action:{action} }); + // }} + // >{displayName} + // + // ); + // })} + // + // ); + // } + + handleOnSearch(value: string) { + const expandedKeys = this.state.root + .getList() + .map((item) => { + if (item.isFolder) return null; + const title = item.text; + if (title.includes(value) && item.parent) { + return item.parent.id; + } + return null; + }) + .filter((item, i, self) => item && self.indexOf(item) === i); + + const root = this.state.root; + root.setVisible(value || ""); + if (value && value.length > 0) { + this.setState({ + root: root, + searchKey: value, + expandedKeys: expandedKeys, + autoExpandParent: true, + }); + } else { + this.setState({ + root: root, + searchKey: "", + expandedKeys: this.state.defaultExpandedKeys, + autoExpandParent: false, + }); + } + + this.forceUpdate(); + } + + selectNode(id: string) { + this.setState({ + selectedKey: id + }); + this.forceUpdate(); + } + + renderItem(node: FileDataNode) { + return (); + } + + render() { + console.log("render Explorer"); + const { onOpenTree, onDeleteTree, workdir } = this.props; + + const root = this.state.root; + if (!workdir || workdir === "" || !root) { + return `请打开workspace.json文件`; + } + + const nodes = [root.getRenderData()]; + + return ( +
+ { + // const value = e.target.value?.toLowerCase(); + // this.handleOnSearch(value); + // }} + onSearch={(value, event) => { + this.handleOnSearch(value?.toLowerCase()); + }} + /> + + { + this.setState({ expandedKeys: expanded }) + this.forceUpdate(); + }} + renderItem={this.renderItem.bind(this)} + onClick={(node: FileDataNode) => { + if (!node.isFolder) { + onOpenTree(node.path); + } + }} + > +
+ ); + } +} diff --git a/tools/behavior3editor/render-process/RegisterNode.ts b/tools/behavior3editor/render-process/RegisterNode.ts new file mode 100644 index 0000000..bda884e --- /dev/null +++ b/tools/behavior3editor/render-process/RegisterNode.ts @@ -0,0 +1,280 @@ +import G6 from "@antv/g6"; +import { toBreakWord } from "../common/Utils"; +import Settings from "../main-process/Settings"; + +const NODE_COLORS: any = { + ["Composite"]: "rgb(91,237,32)", + ["Decorator"]: "rgb(218,167,16)", + ["Condition"]: "rgb(228,20,139)", + ["Action"]: "rgb(91,143,249)", + ["Other"]: "rgb(112,112,112)", +}; + +export default function (settings: Settings) { + const collapseStyle: any = { + "collapse-icon": { + // symbol: G6.Marker.expand, + fill: "blue", + }, + }; + G6.registerNode( + "TreeNode", + { + options: { + type: "rect", + labelCfg: { + style: { + fill: "blue", + fontSize: 10, + }, + }, + style: { + fill: "white", + }, + stateStyles: { + selected: { + "main-box": { + stroke: "yellow", + lineWidth: 5, + }, + }, + hover: { + stroke: "white", + lineWidth: 3, + }, + dragSrc: { + fill: "gray", + }, + dragRight: { + "drag-right": { + fillOpacity: 0.6, + }, + }, + dragUp: { + "drag-up": { + fillOpacity: 0.6, + }, + }, + dragDown: { + "drag-down": { + fillOpacity: 0.6, + }, + }, + }, + }, + draw(cfg, group) { + const nodeConf = settings.getNodeConf(cfg.name as string); + const classify = nodeConf.type || "Other"; + const color = NODE_COLORS[classify] || "Other"; + var size = cfg.size ? (cfg.size as number[]) : [150, 40]; + const w = size[0]; + const h = size[1]; + const x0 = -w / 2; + const y0 = -h / 2; + const r = 2; + const shape = group.addShape("rect", { + attrs: { + x: x0, + y: y0, + width: w, + height: h, + stroke: color, + lineWidth: 2, + fill: "white", + radius: r, + }, + name: "main-box", + draggable: true, + }); + + // name bg + group.addShape("rect", { + attrs: { + x: x0 + 1.5, + y: y0 + 1.5, + width: w - 3, + height: 16, + fill: color, + // radius: r, + }, + name: "name-bg", + draggable: true, + }); + + // id text + group.addShape("text", { + attrs: { + textBaseline: "top", + x: x0 - 3, + y: -8, + fontSize: 20, + lineHeight: 20, + text: cfg.id, + textAlign: "right", + fill: "white", + stroke: "black", + lineWidth: 2, + }, + name: "id-text", + }); + + // icon + var img = `./static/icons/${classify}.svg`; + group.addShape("image", { + attrs: { + x: x0 + 3, + y: y0 + 2, + height: 14, + width: 14, + img, + }, + name: "node-icon", + }); + + // name text + group.addShape("text", { + attrs: { + textBaseline: "top", + x: x0 + 18, + y: y0 + 4, + fontWeight: 800, + lineHeight: 20, + text: cfg.name, + fill: "black", + }, + name: "name-text", + }); + + var x = x0 + 2; + var y = y0 + 23; + // desc text + const desc = cfg.desc || nodeConf.desc; + if (desc) { + group.addShape("text", { + attrs: { + textBaseline: "top", + x, + y, + lineHeight: 20, + fontWeight: 800, + text: `备注:${desc}`, + fill: "black", + }, + name: "desc-text", + }); + } + + const args: any = cfg.args; + if (nodeConf.args && args && Object.keys(args).length > 0) { + const { str, line } = toBreakWord(`参数:${JSON.stringify(args)}`, 35); + group.addShape("text", { + attrs: { + textBaseline: "top", + x, + y: y + 20, + w, + lineHeight: 20, + text: str, + fill: "black", + }, + name: "args-text", + }); + y += 20 * line; + } + + const input: [] = cfg.input ? (cfg.input as []) : []; + if (nodeConf.input && input.length > 0) { + const { str, line } = toBreakWord(`输入:${JSON.stringify(input)}`, 35); + group.addShape("text", { + attrs: { + textBaseline: "top", + x, + y: y + 20, + lineHeight: 20, + text: str, + fill: "black", + }, + name: "input-text", + }); + y += 20 * line; + } + + const output: [] = cfg.output ? (cfg.output as []) : []; + if (nodeConf.output && output.length > 0) { + const { str, line } = toBreakWord(`输出:${JSON.stringify(output)}`, 35); + group.addShape("text", { + attrs: { + textBaseline: "top", + x, + y: y + 20, + lineHeight: 20, + text: str, + fill: "black", + }, + name: "output-text", + }); + y += 20 * line; + } + + if (Array.isArray(cfg.children) && cfg.children.length > 0) { + group.addShape("marker", { + attrs: { + x: w / 2, + y: 0, + r: 6, + symbol: G6.Marker.collapse, + stroke: "#666", + lineWidth: 1, + fill: "#fff", + }, + name: "collapse-icon", + }); + } + + group.addShape("rect", { + name: "drag-up", + attrs: { + x: -w / 2, + y: -h / 2, + width: w, + height: h / 2, + fill: "blue", + fillOpacity: 0, + }, + draggable: true, + // visible: false, + }); + + group.addShape("rect", { + name: "drag-down", + attrs: { + x: -w / 2, + y: 0, + width: w, + height: h / 2, + fill: "blue", + fillOpacity: 0, + }, + draggable: true, + // visible: false, + }); + + group.addShape("rect", { + name: "drag-right", + attrs: { + x: w * 0.1, + y: -h / 2, + width: w * 0.4, + height: h, + fill: "blue", + fillOpacity: 0, + }, + draggable: true, + // visible: false, + }); + return shape; + }, + }, + "single-node" + ); +} diff --git a/tools/behavior3editor/render-process/TreeTabs.tsx b/tools/behavior3editor/render-process/TreeTabs.tsx new file mode 100644 index 0000000..a56f1a5 --- /dev/null +++ b/tools/behavior3editor/render-process/TreeTabs.tsx @@ -0,0 +1,170 @@ +import Editor from "./Editor"; +import React, { Component } from "react"; +import { Layout, Tabs, message } from "antd"; +import { ipcRenderer, remote } from "electron"; +import * as path from "path"; + +import "antd/dist/antd.dark.css"; +import MainEventType from "../common/MainEventType"; +import { BehaviorTreeModel } from "../common/BehaviorTreeModel"; + +const { TabPane } = Tabs; + +interface TreeTabsProps { + onTabSelected: (path: string) => void; +} + +interface TreeInfo { + filepath: string; + unsave?: boolean; +} + +interface TreeTabsState { + trees: TreeInfo[]; + curTree?: string; +} + +export default class TreeTabs extends Component { + state: TreeTabsState = { + trees: [], + }; + + editors: { [path: string]: Editor } = {}; + + componentDidMount() { + ipcRenderer.on(MainEventType.CREATE_NODE, (event: any, name: any) => { + const editor = this.getCurEditor(); + editor?.createNode(name); + }); + + ipcRenderer.on(MainEventType.DELETE_NODE, () => { + const editor = this.getCurEditor(); + editor?.deleteNode(); + }); + + ipcRenderer.on(MainEventType.COPY_NODE, () => { + const editor = this.getCurEditor(); + editor?.copyNode(); + }); + + ipcRenderer.on(MainEventType.PASTE_NODE, () => { + const editor = this.getCurEditor(); + editor?.pasteNode(); + }); + + ipcRenderer.on(MainEventType.SAVE, (event: any) => { + const { curTree: curPath } = this.state; + if (!curPath) { + return; + } + const editor = this.editors[curPath]; + editor.save(); + message.success("已保存"); + }); + + ipcRenderer.on(MainEventType.SAVE_ALL, (event: any) => { + for (let k in this.editors) { + let editor = this.editors[k]; + editor.save(); + } + message.success("已保存所有行为树"); + }); + + ipcRenderer.on(MainEventType.UNDO, () => { + const editor = this.getCurEditor(); + editor?.undo(); + }); + + ipcRenderer.on(MainEventType.REDO, () => { + const editor = this.getCurEditor(); + editor?.redo(); + }); + } + + componentDidUpdate() { + setTimeout(() => { + this.props.onTabSelected(this.state.curTree); + }, 500); + } + + getOpenTreesModel() { + const trees: BehaviorTreeModel[] = [] + for (let k in this.editors) { + let editor = this.editors[k]; + if (editor) { + trees.push(editor.save()); + } + } + return trees; + } + + getCurEditor() { + const { curTree: curPath } = this.state; + if (!curPath) { + return; + } + return this.editors[curPath]; + } + + openFile(path: string) { + if (!this.state.trees.find(e => e.filepath == path)) { + const trees = this.state.trees; + trees.push({ filepath: path }); + this.setState({ trees, curTree: path }); + } else { + this.setState({ curTree: path }); + } + } + + closeFile(path: string) { + let trees = this.state.trees; + trees = trees.filter(tree => tree.filepath != path); + const length = trees.length; + this.setState({ trees: trees, curTree: length > 0 ? trees[0].filepath : null }); + } + + render() { + const { trees, curTree } = this.state; + console.log("render tabs", trees); + if (!curTree) { + return
; + } + this.editors = {}; + return ( + { + this.setState({ curTree: activeKey }); + }} + onEdit={(targetKey, action) => { + if (action == "remove") { + this.closeFile(targetKey as string); + } + }} + > + {trees.map((tree) => { + return ( + + { + if(tree.unsave != unsave) { + tree.unsave = unsave; + this.forceUpdate(); + } + }} + ref={(ref) => { + this.editors[tree.filepath] = ref; + }} + /> + + ); + })} + + ); + } +} diff --git a/tools/behavior3editor/render-process/index.css b/tools/behavior3editor/render-process/index.css new file mode 100644 index 0000000..d18597e --- /dev/null +++ b/tools/behavior3editor/render-process/index.css @@ -0,0 +1,78 @@ +body { + /* background-color: #242424; */ + background-color: rgb(20, 20, 20); + padding : 0px; +} + +.body { + width : 100%; + height : 100%; + position: fixed; +} + +.sider { + height : 100%; + overflow: hidden; +} + +.sider:hover { + overflow-y : auto; +} + +.content { + width : 100%; + height : 100%; + display: flex; + /* background-color: #242424; */ + background-color: rgb(20, 20, 20); + +} + +.tabs { + width: 100%; + padding: 0; +} + +::-webkit-scrollbar { + background-color: rgb(20, 20, 20); +} + +::-webkit-scrollbar-thumb { + height : 50px; + background-color: #818181; +} + + +[data-theme="dark"] .site-tree-search-value { + color: #d84a1b; + } + + +.explorer-node { + margin: 0; + padding: 0; + line-height: 20px; + white-space: nowrap; + list-style: none; + outline: 0; + color: #ffffff; +} + +.explorer-node-selected { + margin: 0; + padding: 0; + line-height: 20px; + white-space: nowrap; + list-style: none; + outline: 0; + color: #ffffff; + background-color: #818181; +} + +.explorer-node-title { + color: #ffffff; +} + +.explorer-node-search-value { + color: #f50; +} diff --git a/tools/behavior3editor/render-process/index.tsx b/tools/behavior3editor/render-process/index.tsx new file mode 100644 index 0000000..0acf475 --- /dev/null +++ b/tools/behavior3editor/render-process/index.tsx @@ -0,0 +1,125 @@ +import * as ReactDOM from "react-dom"; +import SyncRequest from "sync-request"; +import React, { Component } from "react"; +import { Layout, message, Tabs } from "antd"; +import { ipcRenderer, remote } from "electron"; +import * as Utils from "../common/Utils"; +import MainEventType from "../common/MainEventType"; +import Settings from "../main-process/Settings"; + +import "antd/dist/antd.dark.css"; +import "./index.css"; +import RegisterNode from "./RegisterNode"; +import TreeTabs from "./TreeTabs"; +import Explorer from "./Explorer"; +import BatchExec from "../common/BatchExec"; + +const { Sider, Content } = Layout; + +interface MainState { + workdir: string; + workspace: string; +} + +export default class Main extends Component { + state: MainState = { + workdir: "", + workspace: "", + }; + + settings: Settings; + tabs: TreeTabs; + explorer: Explorer; + session = 0; + + componentDidMount() { + this.updateSettings(); + RegisterNode(this.settings); + + ipcRenderer.on(MainEventType.OPEN_FILE, (event: any, path: any) => { + this.setState({ curPath: path }); + }); + + ipcRenderer.on(MainEventType.OPEN_DIR, (event: any, workdir: any, workspace: string) => { + console.log("on open workspace", workspace); + document.title = workspace; + this.setState({ workdir, workspace }); + }); + + ipcRenderer.on(MainEventType.RELOAD_SERVER, () => { + const serverModel = this.settings.serverModel; + if (!serverModel) { + message.warning("没有配置服务器"); + return; + } + SyncRequest("POST", serverModel.host, { + body: JSON.stringify({ + cmd:"btree.reload", + data: { + trees: this.tabs.getOpenTreesModel() + }, + session: this.session++, + timestamp: Date.now(), + }) + }); + message.success("服务器已更新"); + }) + + ipcRenderer.on(MainEventType.BATCH_EXEC, (event: any, path: string)=>{ + BatchExec(path, this.state.workdir); + }) + + console.log("workdir", this.settings.curWorkspace.getWorkdir()); + setTimeout(() => { + this.setState({ + workdir: this.settings.curWorkspace.getWorkdir(), + workspace: this.settings.curWorkspace.getFilepath(), + }); + }, 50); + } + + updateSettings() { + this.settings = Utils.getRemoteSettings(); + } + + render() { + console.log("render main"); + const { workdir, workspace } = this.state; + document.title = `行为树编辑器 - ${workspace}`; + return ( + + + {workdir !== "" ? ( + { + this.explorer = ref; + }} + workdir={workdir} + onOpenTree={(path) => { + this.tabs.openFile(path); + }} + onDeleteTree={(path) => { + this.tabs.closeFile(path); + }} + /> + ) : ( + "Please Open Workspace" + )} + + + { + this.tabs = ref; + }} + onTabSelected={(path)=>{ + this.explorer.selectNode(path); + } + } + /> + + + ); + } +} + +ReactDOM.render(
, document.getElementById("root") as HTMLElement); diff --git a/tools/behavior3editor/sample/node-config.json b/tools/behavior3editor/sample/node-config.json new file mode 100644 index 0000000..622777a --- /dev/null +++ b/tools/behavior3editor/sample/node-config.json @@ -0,0 +1 @@ +[{"name":"AlwaysFail","type":"Decorator","doc":"+ 只能有一个子节点,多个仅执行第一个\n+ 不管子节点是否成功都返回失败\n","desc":"始终返回失败"},{"name":"AlwaysSuccess","type":"Decorator","doc":"+ 只能有一个子节点,多个仅执行第一个\n+ 不管子节点是否成功都返回成功\n","desc":"始终返回成功"},{"name":"Attack","type":"Action","input":["{目标}"],"desc":"攻击"},{"name":"Cmp","type":"Condition","doc":"+ 若值为空,返回失败\n+ 非整数类型可能会报错\n","input":["值(int)"],"args":[{"name":"value","desc":"值","type":"lua?"},{"name":"gt","desc":">","type":"int?"},{"name":"ge","desc":">=","type":"int?"},{"name":"eq","desc":"==","type":"int?"},{"name":"le","desc":"<=","type":"int?"},{"name":"lt","desc":"<","type":"int?"}],"desc":"比较值大小"},{"name":"FindEnemy","output":["目标单位"],"type":"Condition","doc":"+ 没找到返回失败\n","args":[{"name":"x","desc":"x","type":"int?"},{"name":"y","desc":"y","type":"int?"},{"name":"w","desc":"宽","type":"int?"},{"name":"h","desc":"高","type":"int?"},{"name":"count","desc":"查找上限","type":"string?"}],"desc":"查找敌人"},{"name":"GetHp","output":["生命值"],"type":"Action","desc":"获取生命值"},{"name":"Idle","type":"Action","desc":"待机"},{"name":"Log","type":"Action","args":[{"name":"str","desc":"日志","type":"string"}],"desc":"打印日志"},{"name":"MoveToPos","type":"Action","args":[{"name":"x","desc":"x","type":"int"},{"name":"y","desc":"y","type":"int"}],"desc":"移动到坐标"},{"name":"MoveToTarget","type":"Action","input":["{目标}"],"desc":"移动到目标"},{"name":"Not","type":"Decorator","doc":"+ 将子节点的返回值取反\n","desc":"取反"},{"name":"Parallel","type":"Composite","doc":"执行所有子节点并返回成功\n","desc":"并行执行"},{"name":"Selector","type":"Composite","doc":"+ 一直往下执行,有子节点返回成功则返回成功,若全部节点返回失败则返回失败\n+ 子节点是或的关系\n","desc":"选择执行"},{"name":"Sequence","type":"Composite","doc":"+ 一直往下执行,有子节点返回成功则返回成功,若全部节点返回失败则返回失败\n+ 子节点是或的关系\n","desc":"顺序执行"},{"name":"Wait","type":"Action","args":[{"name":"time","desc":"时间/tick","type":"int"}],"desc":"等待"}] \ No newline at end of file diff --git a/tools/behavior3editor/sample/scripts/test.js b/tools/behavior3editor/sample/scripts/test.js new file mode 100644 index 0000000..7022fd3 --- /dev/null +++ b/tools/behavior3editor/sample/scripts/test.js @@ -0,0 +1,16 @@ +/* 批处理脚本 */ +/* + 如果有processTree,则先运行prcessTree,如果有返回,则进行后面的针对每个节点运行processNode + 如果没有定义processTree,则直接针对每个节点运行processNode +*/ +({ + processTree: (tree) => { + console.log(`processTree ${tree.name}`); + if (tree.name == "hero") { + return tree; + } + }, + processNode: (node, tree) => { + console.log(`processNode ${tree.name}.${node.id}`); + } +}) \ No newline at end of file diff --git a/tools/behavior3editor/sample/workdir/hero.json b/tools/behavior3editor/sample/workdir/hero.json new file mode 100644 index 0000000..3423b43 --- /dev/null +++ b/tools/behavior3editor/sample/workdir/hero.json @@ -0,0 +1 @@ +{"name":"hero","root":{"id":1,"name":"Selector","desc":"英雄测试AI","args":{},"children":[{"id":2,"name":"Sequence","desc":"攻击","args":{},"children":[{"id":3,"name":"FindEnemy","args":{"x":0,"y":0,"w":100,"h":50},"output":["enemy"]},{"id":4,"name":"Attack","args":{},"input":["enemy"]},{"id":5,"name":"Wait","args":{"time":10}}]},{"id":6,"name":"Sequence","desc":"移动","args":{},"children":[{"id":7,"name":"FindEnemy","args":{"w":1000,"h":500,"x":0,"y":0},"output":["enemy"]},{"id":8,"name":"MoveToTarget","args":{},"input":["enemy"]}]},{"id":9,"name":"Sequence","desc":"逃跑","args":{},"children":[{"id":10,"name":"GetHp","args":{},"output":["hp"]},{"id":11,"name":"Cmp","args":{"lt":50},"input":["hp"]},{"id":12,"name":"MoveToPos","args":{"x":0,"y":0}}]},{"id":13,"name":"Idle"}]},"desc":"英雄测试AI"} \ No newline at end of file diff --git a/tools/behavior3editor/sample/workdir/monster.json b/tools/behavior3editor/sample/workdir/monster.json new file mode 100644 index 0000000..6fc4aa6 --- /dev/null +++ b/tools/behavior3editor/sample/workdir/monster.json @@ -0,0 +1 @@ +{"name":"monster","root":{"id":1,"name":"Sequence","desc":"怪物测试AI","args":{},"children":[{"id":2,"name":"Sequence","desc":"攻击","args":{},"children":[{"id":3,"name":"GetHp","args":{},"output":["hp"]},{"id":4,"name":"Cmp","args":{"gt":50},"input":["hp"]},{"id":5,"name":"Log","desc":"攻击","args":{"str":"Attack!"}}]},{"id":6,"name":"Log","desc":"逃跑","args":{"str":"Run!"}}]},"desc":"怪物测试AI"} \ No newline at end of file diff --git a/tools/behavior3editor/sample/workspace.json b/tools/behavior3editor/sample/workspace.json new file mode 100644 index 0000000..9a58516 --- /dev/null +++ b/tools/behavior3editor/sample/workspace.json @@ -0,0 +1,5 @@ +{ + "isRelative": true, + "nodeConfPath": "node-config.json", + "workdir": "workdir" +} \ No newline at end of file diff --git a/tools/behavior3editor/static/icons/Action.svg b/tools/behavior3editor/static/icons/Action.svg new file mode 100644 index 0000000..96808ad --- /dev/null +++ b/tools/behavior3editor/static/icons/Action.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tools/behavior3editor/static/icons/Composite.svg b/tools/behavior3editor/static/icons/Composite.svg new file mode 100644 index 0000000..be106d7 --- /dev/null +++ b/tools/behavior3editor/static/icons/Composite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tools/behavior3editor/static/icons/Condition.svg b/tools/behavior3editor/static/icons/Condition.svg new file mode 100644 index 0000000..c6fafe1 --- /dev/null +++ b/tools/behavior3editor/static/icons/Condition.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tools/behavior3editor/static/icons/Decorator.svg b/tools/behavior3editor/static/icons/Decorator.svg new file mode 100644 index 0000000..d670472 --- /dev/null +++ b/tools/behavior3editor/static/icons/Decorator.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tools/behavior3editor/static/icons/Other.svg b/tools/behavior3editor/static/icons/Other.svg new file mode 100644 index 0000000..5666887 --- /dev/null +++ b/tools/behavior3editor/static/icons/Other.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tools/behavior3editor/tsconfig.json b/tools/behavior3editor/tsconfig.json new file mode 100644 index 0000000..e2e4277 --- /dev/null +++ b/tools/behavior3editor/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "outDir": "./dist/", + "sourceMap": true, + "esModuleInterop": true, + "noImplicitAny": true, + "module": "commonjs", + "target": "es6", + "jsx": "react" + }, + "include": [ + "./common/**/*", + "./main-process/**/*", + "./render-process/**/*", + "./fs-tree-view/**/*", + ], + "exclude": [ + "node_modules", + "dist" + ] +} \ No newline at end of file diff --git a/tools/behavior3editor/webpack.config.js b/tools/behavior3editor/webpack.config.js new file mode 100644 index 0000000..885e967 --- /dev/null +++ b/tools/behavior3editor/webpack.config.js @@ -0,0 +1,52 @@ +const CopyPlugin = require("copy-webpack-plugin"); + +module.exports = { + mode: "development", + entry: { + "bundle": ["./render-process/index.tsx"], + "main": ["./main-process/MainProcess.tsx"] + }, + output: { + filename: "[name].js", + path: __dirname + "/dist" + }, + + devtool: "source-map", + + resolve: { + extensions: [".ts", ".tsx", ".js", ".json"] + }, + + module: { + rules: [ + { test: /\.tsx?$/, loader: "ts-loader" }, + { test: /\.(jpe?g|png|gif|svg)$/i, loader: "file-loader" }, + { enforce: "pre", test: /\.js$/, loader: "source-map-loader" }, + { + test: /\.less$/, + include: /node_modules/, + loaders: ['style-loader','css-loader','less-loader'] + }, + { + test: /\.css$/, + loaders: ['style-loader', 'css-loader'] + }, + {//https://github.com/ashtuchkin/iconv-lite/issues/205 + test: /node_modules[\/\\](iconv-lite)[\/\\].+/, + resolve: { + aliasFields: ['main'] + } + }, + + ] + }, + + plugins: [ + ], + + target: "electron-renderer", + + externals: [{ + 'electron-reload': 'require("electron-reload")' + }] +}; \ No newline at end of file diff --git a/tools/lqc/cli/app.lua b/tools/lqc/cli/app.lua new file mode 100644 index 0000000..21df7dd --- /dev/null +++ b/tools/lqc/cli/app.lua @@ -0,0 +1,127 @@ +--- Module which contains the main logic of the CLI application +-- @module lqc.cli.app +-- @alias app +local Vector = require 'lqc.helpers.vector' +local reduce = require 'lqc.helpers.reduce' +local filter = require 'lqc.helpers.filter' +local fs = require 'lqc.helpers.fs' +local random = require 'lqc.random' +local lqc = require 'lqc.quickcheck' +local loader = require 'lqc.cli.loader' +local arg_parser = require 'lqc.cli.arg_parser' +local report = require 'lqc.report' + +-- File used for remembering last used seed for generating test cases. +local check_file = '.lqc' + +--- Tries to read the last quickcheck seed. +-- @return the last used seed (or nil on error). +local function read_from_check_file() + return fs.read_file(check_file) +end + +--- Writes the seed to the check file (.lqc.lua). +-- @param seed Seed to write to the file +local function write_to_check_file(seed) + fs.write_file(check_file, seed) +end + +--- Initializes the random seed; either with last used value (--check) or a +-- specific seed (-s, --seed) or default (current timestamp) +-- @param config Config which specifies how seed initialization should be handled +local function initialize_random_seed(config) + local seed = config.seed + if config.check then -- redo last generated test run (if --check specified) + seed = read_from_check_file() + end + local actual_used_seed = random.seed(seed) + write_to_check_file(actual_used_seed) + report.report_seed(actual_used_seed) +end + +--- Depending on the config, returns a list of files that should be executed. +-- @return List of script files that should be executed. +local function find_files(files_or_dirs) + return reduce(files_or_dirs, Vector.new(), function(file_or_dir, acc) + if fs.is_file(file_or_dir) then + return acc:push_back(file_or_dir) + end + + -- directory + return acc:append(Vector.new(fs.find_files(file_or_dir))) + end):to_table() +end + +--- Checks if a file is a file ending in .lua or .moon (if moonscript available) +-- @param file Path to a file +-- @return true if it has the correct extension; otherwise false. +local function is_script_file(file) + return fs.is_lua_file(file) or fs.is_moonscript_file(file) +end + +--- Filters out all files not ending in .lua or .moon +-- @param files list of all files +-- @return filtered list of files, containing only the Lua and Moonscript scripts +local function find_script_files(files) + return filter(files, is_script_file) +end + +--- Executes all scripts, specified by a table of file paths. +-- @param files List of scripts to execute. +local function execute_scripts(files) + for _, file in pairs(files) do + -- TODO clear environment each time? + local script = loader.load_script(file) + script() + end +end + +--- Verifies all properties, with the work divided over X number of threads. +-- @param numthreads Number of threads to divide the work over. +local function verify_properties(numthreads) + if numthreads == 1 then + lqc.check() + return + end + + lqc.check_mt(numthreads) +end + +--- Shows the test output (statistics) +local function show_output() + report.report_errors() + report.report_summary() +end + +local app = {} + +-- Exits the application +function app.exit() + os.exit(lqc.failed and 1 or 0) +end + +--- Main function of the CLI application +-- 1. parse arguments +-- 2. find all files needed to run +-- 3. initialize random seed +-- 4. run 1 file, or all files in a directory (depending on args) +-- 5. execute properties (lqc.check) +-- 6. show output +-- @param cli_args Table containing the commandline arguments +function app.main(cli_args) + local config = arg_parser.parse(cli_args or {}) + local files = find_files(config.files_or_dirs) + local script_files = find_script_files(files) + + initialize_random_seed(config) + lqc.init(config.numtests, config.numshrinks) + report.configure(config.colors) + execute_scripts(script_files) + verify_properties(config.threads) + show_output() + + app.exit() +end + +return app + diff --git a/tools/lqc/cli/arg_parser.lua b/tools/lqc/cli/arg_parser.lua new file mode 100644 index 0000000..048a3e0 --- /dev/null +++ b/tools/lqc/cli/arg_parser.lua @@ -0,0 +1,43 @@ +--- Module for parsing of commandline arguments +-- @module lqc.cli.arg_parser +-- @alias lib +local argparse = require 'argparse' +local config = require 'lqc.config' + +-- Module for easily parsing list of command line arguments + +local name_of_executable = 'lqc' +local help_info = 'Property based testing tool written in Lua' +local parser = argparse(name_of_executable, help_info) +parser.error = function(msg) + error(msg) +end + +-- Converts a string to an integer +-- Returns an integer representation of the input string or raises an error on failure. +local function str_to_int(x) + return tonumber(x) +end + +parser:argument('files_or_dirs', + 'List of input files or directories (recursive search) used for testing, default = "."', nil, nil, '*') +parser:mutex(parser:flag('--check', 'Re-checks the set of tests generated by the last seed'), parser:option('--seed -s', + 'Value of the random seed to use, default = seed based on current time', nil, str_to_int)) +parser:option('--numtests', 'Number of iterations per property, default = 100', nil, str_to_int) +parser:option('--numshrinks', 'Number of shrinks per failing property, default = 100', nil, str_to_int) +parser:flag('--colors -c', "Enable coloring of test output, default = disabled (doesn't work on Windows!).") +parser:option('--threads -t', "Executes properties in parallel, default = single-threaded (requires Lua Lanes!).", nil, + str_to_int) + +local lib = {} + +--- Parses the arguments +-- @return a table containing the config specified by the user; +-- raises an error if parsing failed. +function lib.parse(args) + local parsed_values = parser:parse(args) + return config.resolve(parsed_values) +end + +return lib + diff --git a/tools/lqc/cli/loader.lua b/tools/lqc/cli/loader.lua new file mode 100644 index 0000000..a49b368 --- /dev/null +++ b/tools/lqc/cli/loader.lua @@ -0,0 +1,77 @@ +--- Module for pre-loading all lqc modules so the user does not have to do this. +-- @module lqc.cli.loader +-- @alias lib +local deep_copy = require 'lqc.helpers.deep_copy' +local fs = require 'lqc.helpers.fs' +local has_moonscript, moonscript = pcall(require, 'moonscript') + +local lib = {} + +-- Prepare new global env for easier use of property based testing library +local new_global_env = deep_copy(_G) +new_global_env.Generator = require 'lqc.generator' +new_global_env.any = require 'lqc.generators.any' +new_global_env.bool = require 'lqc.generators.bool' +new_global_env.byte = require 'lqc.generators.byte' +new_global_env.char = require 'lqc.generators.char' +new_global_env.float = require 'lqc.generators.float' +new_global_env.int = require 'lqc.generators.int' +new_global_env.str = require 'lqc.generators.string' +new_global_env.tbl = require 'lqc.generators.table' +new_global_env.random = require 'lqc.random' +new_global_env.property = require 'lqc.property' +new_global_env.fsm = require 'lqc.fsm' +new_global_env.state = require 'lqc.fsm.state' +new_global_env.command = require 'lqc.fsm.command' +do + local lqc_gen = require 'lqc.lqc_gen' + new_global_env.choose = lqc_gen.choose + new_global_env.frequency = lqc_gen.frequency + new_global_env.elements = lqc_gen.elements + new_global_env.oneof = lqc_gen.oneof +end + +--- Compatibility workaround: setfenv is removed from Lua for versions > 5.1. +-- This function aims to provide same functionality. +-- Based mostly on http://leafo.net/guides/setfenv-in-lua52-and-above.html +-- @param func function for which the environment should be changed +-- @param new_env table containing the new environment to be set +local function setfenv_compat(func, new_env) + local idx = 1 + repeat + local name = debug.getupvalue(func, idx) + if name == '_ENV' then + debug.upvaluejoin(func, idx, function() + return new_env + end, 1) + end + idx = idx + 1 + until name == '_ENV' or name == nil + + return func +end + +local setfenv = setfenv or setfenv_compat + +--- Loads a script, sets a new environment (for easier property based testing), +-- @param file_path Path to the file containing a Lua/Moonscript script +-- @return the modified script which can be called as a function. +function lib.load_script(file_path) + -- Check if Moonscript file and if Moonscript available + if fs.is_moonscript_file(file_path) then + if not has_moonscript then + return function() + end + end -- return empty 'script' + + local script = moonscript.loadfile(file_path) + return setfenv(script, new_global_env) + end + + -- Lua file + local script = loadfile(file_path) + return setfenv(script, new_global_env) +end + +return lib + diff --git a/tools/lqc/config.lua b/tools/lqc/config.lua new file mode 100644 index 0000000..dcdf222 --- /dev/null +++ b/tools/lqc/config.lua @@ -0,0 +1,46 @@ +--- Helper module for managing the config in the application. +-- @module lqc.config +-- @alias config +local config = {} + +--- Helper function for getting the default random seed. +-- +-- @return default seed used by the application (the current timestamp). +function config.default_seed() + return os.time() +end + +local default_config = { + files_or_dirs = {'.'}, + seed = config.default_seed(), + numtests = 100, + numshrinks = 100, + colors = false, + threads = 1, + check = false, +} + +--- Checks if the argument is empty (nil or {}). +-- +-- @param x Argument to check +-- @return true if arg is empty; otherwise false. +local function is_empty_arg(x) + return x == nil or (type(x) == 'table' and #x == 0) +end + +--- Determines the config to use based on the table of supplied values. +-- If no value is supplied for a specific setting, a default value is used +-- (see top of file). +-- +-- @return the updated config +function config.resolve(values) + for _, arg_name in ipairs {'files_or_dirs', 'seed', 'numtests', 'numshrinks', 'colors', 'threads', 'check'} do + if is_empty_arg(values[arg_name]) then + values[arg_name] = default_config[arg_name] + end + end + return values +end + +return config + diff --git a/tools/lqc/fsm.lua b/tools/lqc/fsm.lua new file mode 100644 index 0000000..4981f9d --- /dev/null +++ b/tools/lqc/fsm.lua @@ -0,0 +1,102 @@ +--- Helper module for specifying finite state machines (FSMs) with. Provides a DSL. +-- @module lqc.fsm +-- @alias fsm +local algorithm = require 'lqc.fsm.algorithm' +local state = require 'lqc.fsm.state' +local lqc = require 'lqc.quickcheck' + +--- Adds a stop state to the list of states. +-- This is a special predefined state that will stop the FSM from generating +-- more state transitions. +-- @param state_list List of states in the FSM (not including stop state) +-- @return the updated state list (variable modified in place). +local function add_stop_state(state_list) + table.insert(state_list, state 'stop' { + precondition = function() + return true + end, -- always succeeds + next_state = function() + return nil + end, -- not used + postcondition = function() + return true + end, -- always succeeds + }) + return state_list +end + +--- Checks if an object is callable (function or functable): +-- @param obj Object to be checked if it is callable +-- @return true if it is callable; otherwise false +local function is_callable(obj) + local type_obj = type(obj) + return type_obj == 'function' or type_obj == 'table' +end + +--- Checks if the FSM table contains a valid specification of a state machine +-- @param fsm_table Table containing FSM information/description +-- @return nil; raises an error message if specification is not valid +local function check_valid_fsm_spec(fsm_table) + if not is_callable(fsm_table.commands) then + error 'Need to provide list of commands to FSM!' + end + if not is_callable(fsm_table.initial_state) then + error 'Need to provide initial state function to FSM!' + end + + local states = fsm_table.states + if type(states) ~= 'table' then + error 'Need to provide a table of possible states of the FSM!' + end + + -- States are already checked in state.lua +end + +local function default_cleanup() +end +local function default_when_fail() +end + +--- Constructs a new FSM +-- @param description text description of the FSM +-- @param fsm_table table containing FSM info +-- @return FSM object +local function new(description, fsm_table) + local FSM = {} + + function FSM.check(_) + return algorithm.check(description, fsm_table) + end + + return FSM +end + +--- Creates a new FSM and inserts it into the list of properties. +-- @param descr Text description of the FSM +-- @param fsm_info_table Table containing information of the FSM +local function fsm(descr, fsm_info_table) + local function fsm_func(fsm_table) + fsm_table.states = add_stop_state(fsm_table.states) + fsm_table.cleanup = fsm_table.cleanup or default_cleanup + fsm_table.when_fail = fsm_table.when_fail or default_when_fail + fsm_table.numtests = fsm_table.numtests or lqc.numtests + fsm_table.numshrinks = fsm_table.numshrinks or lqc.numshrinks + + check_valid_fsm_spec(fsm_table) + local new_fsm = new(descr, fsm_table) + table.insert(lqc.properties, new_fsm) + end + + if fsm_info_table then + -- Called normally (most likely from Moonscript) + fsm_func(fsm_info_table) + return function() + end + end + + -- Called with DSL syntax + return fsm_func +end + +return fsm + diff --git a/tools/lqc/fsm/action.lua b/tools/lqc/fsm/action.lua new file mode 100644 index 0000000..eb34db6 --- /dev/null +++ b/tools/lqc/fsm/action.lua @@ -0,0 +1,37 @@ +--- Module for describing an action in a 'declarative' way +-- @classmod lqc.fsm.action +-- @alias Action +local Action = {} +local Action_mt = { + __index = Action, +} + +--- Creates a new action. +-- @param var (Symbolic) variable to store the result of the action in +-- @param cmd Command that was called during this action +-- @param command_generator Generator that generated the command, used for shrinking the command +-- @return new action object +function Action.new(var, cmd, command_generator) -- TODO rename to args_generators + if var == nil then + error 'Need to provide variable to action object!' + end + if cmd == nil then + error 'Need to provide command to action object!' + end + + local action = { + variable = var, + command = cmd, + cmd_gen = command_generator, + } + return setmetatable(action, Action_mt) +end + +--- Returns a string representation of the action +-- @return string representation of the action +function Action:to_string() + return '{ set, ' .. self.variable:to_string() .. ', ' .. self.command:to_string() .. ' }' +end + +return Action + diff --git a/tools/lqc/fsm/algorithm.lua b/tools/lqc/fsm/algorithm.lua new file mode 100644 index 0000000..989f605 --- /dev/null +++ b/tools/lqc/fsm/algorithm.lua @@ -0,0 +1,471 @@ +--- Module describing the algorithm used by the FSM properties. +-- @module lqc.fsm.algorithm +-- @alias lib +local Vector = require 'lqc.helpers.vector' +local Var = require 'lqc.fsm.var' +local Command = require 'lqc.fsm.command' +local Action = require 'lqc.fsm.action' +local random = require 'lqc.random' +local deep_copy = require 'lqc.helpers.deep_copy' +local report = require 'lqc.report' +local unpack = unpack or table.unpack -- for compatibility reasons + + +local lib = {} + +--- Creates a small helper object that keeps track of a counter +-- @return counter object +function lib.make_counter() + local Counter = { + val = 1, + increase = function(self) + self.val = self.val + 1 + end, + value = function(self) + return self.val + end, + } + return setmetatable(Counter, { + __index = Counter, + }) +end + +--- Checks if x lies between min and max +-- @param x A number +-- @param min Minimum value +-- @param max Maximum value +-- @return true if min <= x and x <= max; otherwise false. +local function is_between(x, min, max) + return min <= x and x <= max +end + +--- Determines how many items should be shrunk +-- @param max_amount Maximum amount of items that are allowed to be shrunk down +-- @return random number between 1 and max_amount (inclusive) +local function shrink_how_many(max_amount) + if max_amount <= 1 then + return 1 + end + return random.between(1, max_amount) +end + +--- Determines if an an action should be marked for deletion +-- @return true if it should be deleted; otherwise false +local function should_select_action() + return random.between(1, 4) == 1 -- 25% chance +end + +--- Finds a specific state in the list of states based on name of the state. +-- @param states List of states to search in +-- @param state_name Name of the state to find +-- @return the state with that name; +-- raises an error if no state is present with the specified name +function lib.find_state(states, state_name) + for i = 1, #states do + local state = states[i] + if state.name == state_name then + return state + end + end + error('State "' .. state_name .. '" not found in list of FSM states!') +end + +--- Finds the next command based on the FSM model and the current state. +-- @param fsm table describing the FSM property +-- @param current_state Variable containing the current state of the FSM +-- @param var_counter Number indicating how many variables have already been used +-- @return 3 values: chosen_command, cmd_generator, updated_current_state +function lib.find_next_action(fsm, current_state, var_counter) + local numtests, commands, states = fsm.numtests, fsm.commands, fsm.states + local cmd_gen = commands(current_state) + + for _ = 1, 100 do -- TODO make configurable? + local cmd = cmd_gen:pick(numtests) + local selected_state = lib.find_state(states, cmd.state_name) + + if selected_state.precondition(current_state, cmd.args) then -- valid command + local variable = Var.new(var_counter:value()) + var_counter:increase() + current_state = selected_state.next_state(current_state, variable, cmd.args) + return Action.new(variable, cmd, cmd_gen), current_state + end + end + + -- Could not find a next action -> stop generating further actions + return Action.new(Var.new(var_counter:value()), Command.stop, nil), current_state +end + +--- Generates a list of steps for a FSM specification +-- @param fsm_table table containing description of a FSM property +-- @return list of generated actions +function lib.generate_actions(fsm_table) + local generated_actions = Vector.new() + local counter = lib.make_counter() + local state = fsm_table.initial_state() + + repeat + local action, next_state = lib.find_next_action(fsm_table, state, counter) + state = next_state + generated_actions:push_back(action) + until action.command.state_name == 'stop' + + return generated_actions +end + +--- Slices of the last actions past index +-- @param action_vector Vector of actions +-- @param index Last position in the vector that should not be removed +-- @return the action vector (modified in place) +function lib.slice_last_actions(action_vector, index) + local action_vector_copy = deep_copy(action_vector) + local last_pos = action_vector_copy:size() - 1 -- -1 since we want to keep stop action! + for i = last_pos, index + 1, -1 do + action_vector_copy:remove_index(i) + end + return action_vector_copy +end + +--- Selects at most 'how_many' amount of actions from the vector of actions +-- to be marked for deletion. +-- @param action_vector Vector containing list of actions +-- @return vector of actions which should be deleted. +function lib.select_actions(action_vector) + local selected_actions, idx_vector = Vector.new(), Vector.new() + if action_vector:size() <= 2 then + return selected_actions, idx_vector + end + local amount = 0 + local size = action_vector:size() - 2 -- don't remove stop action, keep atleast 1 other action + local how_many = shrink_how_many(size) + + for i = 1, size do + if amount >= how_many then + break + end + if should_select_action() then -- TODO make this a variable function and use a while loop? + idx_vector:push_back(i) + amount = amount + 1 + end + end + + for i = 1, amount do + selected_actions:push_back(action_vector:get(idx_vector:get(i))) + end + + return selected_actions, idx_vector +end + +--- Removes all elements of 'which_actions' from 'action_vector' +-- @param action_vector Vector of actions from which actions will be removed +-- @param which_actions actions to be removed +-- @return an updated vector +function lib.delete_actions(action_vector, which_actions) + local action_vector_copy = deep_copy(action_vector) + for i = 1, which_actions:size() do + action_vector_copy:remove(which_actions:get(i)) + end + return action_vector_copy +end + +--- Does the actual execution of the FSM by executing the list of actions +-- If one of the postconditions fail after an action is applied, then the +-- actions will be shrunk down to a simpler scenario. +-- At the end, the state is cleaned up by the cleanup-callback. +-- Returns 4 values: +-- 1) true if the FSM property succeeded for these actions; false otherwise. +-- 2) index of last successful step (1-based) +-- 3) state of the model right before the failing action +-- 4) result of the last failing action +-- @param fsm_table table containing description of a FSM property +-- @param generated_actions list of actions to be executed for this FSM +-- @return 4 values: +-- 1. bool: true = FSM succeeded; false = FSM failed +-- 2. Amount of actions executed +-- 3. Last state of the FSM +-- 4. Last result (return value of last command) +function lib.execute_fsm(fsm_table, generated_actions) + local state = fsm_table.initial_state() + local last_state, last_result = state, nil + + for i = 1, generated_actions:size() do + local command = generated_actions:get(i).command + local selected_state = lib.find_state(fsm_table.states, command.state_name) + local result = command.func(unpack(command.args)) -- side effects happen here + local updated_state = selected_state.next_state(state, result, command.args) + + last_state, last_result = state, result + + -- and verify the model matches the actual system + -- NOTE: the state passed in is the state that the system had BEFORE + -- executing this specific action! + if not selected_state.postcondition(state, result, command.args) then + fsm_table.cleanup(state) + return false, i, last_state, last_result + end + + state = updated_state -- update model + + end + + fsm_table.cleanup(state) + return true, generated_actions:size() - 1, last_state, last_result +end + +--- Is the list of actions valid to execute on this FSM? +-- Replays sequence symbolically to verify if it is indeed valid. +-- @param fsm_table table containing description of a FSM property +-- @param action_vector list of actions to be checked +-- @return true if list of actions valid; otherwise false. +function lib.is_action_sequence_valid(fsm_table, action_vector) + local states = fsm_table.states + local state = fsm_table.initial_state() + + for i = 1, action_vector:size() do + local action = action_vector:get(i) + local selected_state = lib.find_state(states, action.command.state_name) + + if not selected_state.precondition(state, action.command.args) then + return false + end + + state = selected_state.next_state(state, action.variable, action.command.args) + end + return true +end + +--- Tries to shrink the list of actions to a simpler form by removing steps of +-- the sequence and checking if it is still valid. +-- The function is recursive and will loop until tries_left is 0. +-- @param fsm_table table containing description of a FSM property +-- @param action_list list of actions to be shrunk down +-- @return 2 values: +-- 1) action_list if shrinking was not possible after X amount of tries; +-- otherwise it will return a shrunk list of actions. +-- 2) list of deleted actions (empty if shrinking failed after X tries) +local function do_shrink_actions(fsm_table, action_list) + local which_actions = lib.select_actions(action_list) + local shrunk_actions = lib.delete_actions(action_list, which_actions) + + if not lib.is_action_sequence_valid(fsm_table, shrunk_actions) then + return action_list, Vector.new() + end + + return shrunk_actions, which_actions +end + +--- Does the shrinking of the FSM actions +-- choose 1 - N steps and delete them from list of actions +-- repeat X amount of times (recursively) +-- @param fsm_table table containing description of a FSM property +-- @param generated_actions List of actions to be shrunk down +-- @param removed_actions Already removed actions +-- @param tries Remaining tries to recusively try shrinking the actions +-- @return the shrunk down list of actions +local function shrink_actions(fsm_table, generated_actions, removed_actions, tries) + if tries == 1 then + return generated_actions, removed_actions + end + + local shrunk_actions, deleted_actions = do_shrink_actions(fsm_table, generated_actions) + local total_removed_actions = removed_actions:append(deleted_actions) + + -- TODO add execute fsm here and shrink deleted actions? + return shrink_actions(fsm_table, shrunk_actions, total_removed_actions, tries - 1) +end + +--- Tries to shrink the list of failing actions by selecting a subset and +-- checking if the combination is now valid and if the FSM still fails or not +-- This is a recursive function. +-- @param fsm_table table containing description of a FSM property +-- @param generated_actions List of actions to be shrunk down +-- @param deleted_actions Already deleted actions +-- @param tries Remaining tries to recusively try shrinking the actions +-- @return 3 things: +-- 1. the shrunk list of actions (or the original list if no better solution was found) +-- 2. the end state of the fsm after these actions +-- 3. result of the last action +local function shrink_deleted_actions(fsm_table, generated_actions, deleted_actions, tries) + if tries == 1 then + return generated_actions + end + + local which_actions = lib.select_actions(deleted_actions) + local shrunk_actions = lib.delete_actions(generated_actions, which_actions) + + -- Retry if invalid sequence + local is_valid = lib.is_action_sequence_valid(fsm_table, shrunk_actions) + if not is_valid then + return shrink_deleted_actions(fsm_table, generated_actions, deleted_actions, tries - 1) + end + + -- Check FSM again + -- if FSM succeeds now, (one or more of) the failing actions have been deleted + -- -> simply retry TODO there is an optimisation possible here.. + -- else FSM still failed -> chosen actions did not matter, ignore them and + -- further try shrinking + local is_successful = lib.execute_fsm(fsm_table, shrunk_actions) + if is_successful then + deleted_actions = lib.delete_actions(deleted_actions, which_actions) + end + return shrink_deleted_actions(fsm_table, shrunk_actions, deleted_actions, tries - 1) +end + +--- Tries to shrink down the list of FSM actions. This function is called +-- recursively until all tries are used. +-- @param fsm_table table containing description of a FSM property +-- @param generated_actions List of actions to be shrunk down +-- @param step last step in the series of actions that succeeded while testing +-- the FSM property +-- @param tries Remaining tries to recusively try shrinking the actions +-- @return list of shrunk down actions +function lib.shrink_fsm_actions(fsm_table, generated_actions, step, tries) + if tries == 1 then + return generated_actions + end + + local sliced_actions = lib.slice_last_actions(generated_actions, step) -- cut off actions after failure.. + local shrunk_actions, deleted_actions = + shrink_actions(fsm_table, sliced_actions, Vector.new(), fsm_table.numshrinks) + if deleted_actions:size() == 0 then + -- shrinking did not help, try again + return lib.shrink_fsm_actions(fsm_table, sliced_actions, step, tries - 1) + end + + -- shrinking did help, retry FSM: + local is_successful1, new_step1 = lib.execute_fsm(fsm_table, shrunk_actions) + if not is_successful1 then + -- FSM still fails, deleted actions can be ignored, try further shrinking + return lib.shrink_fsm_actions(fsm_table, shrunk_actions, new_step1, tries - 1) + end + + -- now FSM works -> faulty action is in the just deleted actions + local shrunk_down_actions = shrink_deleted_actions(fsm_table, sliced_actions, deleted_actions, fsm_table.numshrinks) + -- retry fsm: + -- if a solution could not be found by shrinking down the deleted actions + -- -> sliced actions is smallest solution found + -- else if FSM still fails after shrinking deleted_actions + -- -> try further shrinking + local is_successful2, new_step2 = lib.execute_fsm(fsm_table, shrunk_down_actions) + local minimal_actions = is_successful2 and sliced_actions or shrunk_down_actions + return lib.shrink_fsm_actions(fsm_table, minimal_actions, new_step2, tries - 1) +end + +--- Select a group of actions to be marked for shrinking down. +-- @param action_list List of actions to be shrunk down. +-- @return vector of indices for the actions which should be removed +local function select_actions_for_arg_shrinking(action_list) + local _, idx_vector = lib.select_actions(action_list) + if idx_vector:size() == 0 and is_between(action_list:size(), 2, 10) then + -- try shrinking 1 action anyway + local idx = random.between(1, action_list:size() - 1) -- don't shrink stop action! + idx_vector:push_back(idx) + end + return idx_vector +end + +--- Does the actual shrinking of the command arguments of a sequence of actions. +-- @param fsm_table table containing description of a FSM property +-- @param action_list List of actions to be shrunk down +-- @return an updated sequence of the action list (original is modified!) with +-- shrunk arguments. +local function shrink_args(fsm_table, action_list) + local idx_vector = select_actions_for_arg_shrinking(action_list) + + for i = idx_vector:size(), 1, -1 do -- shrunk from end to beginning (most likely to succeed) + local idx = idx_vector:get(i) + local action = action_list:get(idx) + + for _ = 1, fsm_table.numshrinks do + local command_copy = action.command -- shallow copy (reference only) + + action.command = action.cmd_gen:shrink(action.command) + -- revert if shrink is not valid + local is_valid = lib.is_action_sequence_valid(fsm_table, action_list) + if not is_valid then + action.command = command_copy; + break + end + end + end + + return action_list +end + +--- Shrinks down the arguments provided to the sequence of actions +-- This function is called recursively until all tries are used. +-- @param fsm_table table containing description of a FSM property +-- @param generated_actions List of actions to be shrunk down +-- @param tries Remaining tries to recusively try shrinking the actions +-- @return +local function shrink_fsm_args(fsm_table, generated_actions, tries) + if tries == 1 then + return generated_actions + end + + local shrunk_actions = shrink_args(fsm_table, deep_copy(generated_actions)) -- , fsm_table.numshrinks) + + -- retry FSM + local is_successful = lib.execute_fsm(fsm_table, shrunk_actions) + if not is_successful then + -- FSM still fails, shrinking of args was successful, try further shrinking + return shrink_fsm_args(fsm_table, shrunk_actions, tries - 1) + end + + -- FSM works now, shrinking of args unsuccessful -> retry + return shrink_fsm_args(fsm_table, generated_actions, tries - 1) +end + +--- Replays the FSM property. +-- @param fsm_table table containing description of a FSM property +-- @param action_vector List of actions to replay +-- @return last state and result while executing the FSM. +local function replay_fsm(fsm_table, action_vector) + local _, _, last_state, last_result = lib.execute_fsm(fsm_table, action_vector) + return last_state, last_result +end + +--- Shrinks the list of generated actions for a given FSM. +-- This is a recursive function which keeps trying for X amount of times. +-- @param fsm_table table containing description of a FSM property +-- @param generated_actions List of actions to be shrunk down +-- @param step Last successful step that was executed before FSM failed +-- @return shrunk list of actions or original action list if shrinking did not help. +local function shrink_fsm(fsm_table, generated_actions, step) + local fsm_shrink_amount = fsm_table.numshrinks + local shrunk_actions = lib.shrink_fsm_actions(fsm_table, generated_actions, step, fsm_shrink_amount) + local shrunk_actions_and_args = shrink_fsm_args(fsm_table, shrunk_actions, fsm_shrink_amount) + local lst_state, lst_result = replay_fsm(fsm_table, shrunk_actions_and_args) + return shrunk_actions_and_args, lst_state, lst_result +end + +--- The main checking function for FSM specifications. +-- Checks a number of times (according to FSM spec) if property is true. +-- If the specification failed, then the result will be shrunk down to a +-- simpler case. +-- @param description string description of the FSM property +-- @param fsm_table table containing description of a FSM property +-- @return nil on success; otherwise a table containing info related to FSM error. +function lib.check(description, fsm_table) + for _ = 1, fsm_table.numtests do + local generated_actions = lib.generate_actions(fsm_table) + + local is_successful, last_step = lib.execute_fsm(fsm_table, generated_actions) + if not is_successful then + report.report_failed() + local shrunk_actions, last_state, last_result = shrink_fsm(fsm_table, generated_actions, last_step) + fsm_table.when_fail(shrunk_actions:to_table(), last_state, last_result) + return { + description = description, + generated_actions = generated_actions, + shrunk_actions = shrunk_actions, + } + end + + report.report_success() + end + + return nil +end + +return lib + diff --git a/tools/lqc/fsm/command.lua b/tools/lqc/fsm/command.lua new file mode 100644 index 0000000..1662028 --- /dev/null +++ b/tools/lqc/fsm/command.lua @@ -0,0 +1,104 @@ +--- Module for representing a command internally in a 'declarative' way +-- @module lqc.fsm.command +-- @alias command +local Gen = require 'lqc.generator' +local random = require 'lqc.random' +local deep_copy = require 'lqc.helpers.deep_copy' + +--- Returns a string representation of the command. +-- @param cmd Command to transform into a string +-- @return string representation of the command +local function stringify(cmd) + local result = {'{ call, ', cmd.state_name} + + local size_result = #result + for i = 1, #cmd.args do + -- TODO allow printing of table etc.. + result[i + size_result] = ', ' .. cmd.args[i] + end + + result[#result + 1] = ' }' + return table.concat(result) +end + +--- Creates a function that picks a random value for each of the generators +-- specified in the argument list. +-- @param state_name Name of the state this command is associated with +-- @param command_func Function to be called when this command is selected +-- @param args_generators List of generators that generate the arguments for command_func +-- @return table with keys { state_name, func, args } +local function pick(state_name, command_func, args_generators) + local function do_pick(num_tests) + local args = {} + for i = 1, #args_generators do + args[i] = args_generators[i]:pick(num_tests) + end + return { + state_name = state_name, + func = command_func, + args = args, + to_string = stringify, + } + end + return do_pick +end + +--- Does the actual shrinking of the args +-- Randomly picks 1 of the arguments and shrinks it, rest stays the same +-- @param prev_args Previously generated args for the command +-- @param args_generators Generators used to generate and shrink the args +-- @return shrunk down argument list +local function shrink_args(prev_args, args_generators) + local idx = random.between(1, #prev_args) + local shrunk_arg = args_generators[idx]:shrink(prev_args[idx]) + local args_copy = deep_copy(prev_args) + args_copy[idx] = shrunk_arg + return args_copy +end + +--- Shrinks the command to a simpler form. +-- Only args are shrunk, state_name and func are unmodified. +-- @param state_name Name of the state this command is associated with +-- @param command_func Function to be called when this command is selected +-- @param args_generators Generators used to generate and shrink the args +-- @return function that does the actual shrinking for this command +local function shrink(state_name, command_func, args_generators) + local function do_shrink(previous) + if #previous.args == 0 then + return previous + end + return { + state_name = state_name, + func = command_func, + args = shrink_args(previous.args, args_generators), + to_string = stringify, + } + end + return do_shrink +end + +--- Creates a new command generator with a state_name, command function +-- and a list of generators (args will be passed into the command_func in the FSM) +-- @param state_name Name of the state this command is associated with +-- @param command_func Function to be called when this command is selected +-- @param args_generators Generators used to generate and shrink the args +-- @return generator for randomly creating a command +local function new(state_name, command_func, args_generators) + local generator = Gen.new(pick(state_name, command_func, args_generators), + shrink(state_name, command_func, args_generators)) + generator.state_name = state_name + return generator +end + +local command = {} +local command_mt = { + __call = function(_, command_tbl) + return new(command_tbl[1], command_tbl[2], command_tbl[3]) + end, +} + +command.stop = new('stop', function() +end, {}) + +return setmetatable(command, command_mt) + diff --git a/tools/lqc/fsm/state.lua b/tools/lqc/fsm/state.lua new file mode 100644 index 0000000..87ec1b9 --- /dev/null +++ b/tools/lqc/fsm/state.lua @@ -0,0 +1,59 @@ +--- Module that provides a DSL for specifying states in the FSM DSL framework. +-- @module lqc.fsm.state +-- @alias make_state +--- Checks if object is callable (a function or a functable) +-- @param obj Object to check +-- @return true if obj is callable; otherwise false. +local function is_callable(obj) + local type_obj = type(obj) + return type_obj == 'function' or type_obj == 'table' +end + +--- Checks if the state contains all the necessary info for a valid state specification +-- @param state table containing precondition, next_state and postcondition +-- @param state_name name of the state +-- @return nil; raises an error if state contains wrong or missing information +local function check_valid_state(state, state_name) + if type(state_name) ~= 'string' then + error 'Missing state name!' + end + if type(state) ~= 'table' then + error 'State should be specified as a table!' + end + if not is_callable(state.precondition) then + error('Need to provide a precondition function to state ' .. state_name .. '!') + end + if not is_callable(state.next_state) then + error('Need to provide a next_state function to state ' .. state_name .. '!') + end + if not is_callable(state.postcondition) then + error('Need to provide a postcondition function to state ' .. state_name .. '!') + end +end + +--- Helper function for specifying a state in the FSM +-- @param state_name Name to assign to the state +-- @param state_information Table containing precondition, next_state, postcondition functions +-- @return new table containing state information, ready to be added to the FSM +local function make_state(state_name, state_information) + local function make_state_helper(state_info) + check_valid_state(state_info, state_name) + return { + name = state_name, + precondition = state_info.precondition, + next_state = state_info.next_state, + postcondition = state_info.postcondition, + } + end + + if state_information ~= nil then + -- Called with normal syntax, directly return result + return make_state_helper(state_information) + end + + -- called with Lua DSL syntax, return closue which returns result + return make_state_helper +end + +return make_state + diff --git a/tools/lqc/fsm/var.lua b/tools/lqc/fsm/var.lua new file mode 100644 index 0000000..739ab46 --- /dev/null +++ b/tools/lqc/fsm/var.lua @@ -0,0 +1,29 @@ +--- Helper module for symbolically representing a variable. +-- @classmod lqc.fsm.var +-- @alias var +local Var = {} +local Var_mt = { + __index = Var, +} + +--- Creates a symbolic representation of a variable. +-- @param value Value of the variable +-- @return a new variable object +function Var.new(value) + if value == nil then + error 'Need to provide a value to Var!' + end + local var = { + value = value, + } + return setmetatable(var, Var_mt) +end + +--- Returns a string representation of the variable +-- @return string representation of the variable +function Var:to_string() + return '{ var, ' .. self.value .. ' }' +end + +return Var + diff --git a/tools/lqc/generator.lua b/tools/lqc/generator.lua new file mode 100644 index 0000000..698a2d2 --- /dev/null +++ b/tools/lqc/generator.lua @@ -0,0 +1,43 @@ +--- Class for generating (custom) generators. +-- @classmod lqc.generator +-- @alias Gen +local Gen = {} +local Gen_mt = { + __index = Gen, +} + +--- Creates a new generator for generating random values. +-- @param pick_func a function that randomly creates a certain datatype. +-- @param shrink_func a function that shrinks (simplifies) a given input based +-- on last input. +-- @return a generator object +-- @see pick +-- @see shrink +function Gen.new(pick_func, shrink_func) + local Generator = { + pick_func = pick_func, + shrink_func = shrink_func, + } + + return setmetatable(Generator, Gen_mt) +end + +--- Generates a new random value based on this generator's pick value. +-- +-- @param numtests amount of times a property will be run, can be used to guide +-- the choosing process. +-- @return a new randomly chosen value +function Gen:pick(numtests) + return self.pick_func(numtests) +end + +--- Shrinks a generated value to a simpler value. +-- +-- @param prev The previously generated value. +-- @return A newly generated value, simplified from the previous value. +function Gen:shrink(prev) + return self.shrink_func(prev) +end + +return Gen + diff --git a/tools/lqc/generators/any.lua b/tools/lqc/generators/any.lua new file mode 100644 index 0000000..05ff896 --- /dev/null +++ b/tools/lqc/generators/any.lua @@ -0,0 +1,30 @@ + +--- Module for generating 'any' random value. +-- @lqc.generators.any +-- @alias new + +local lqc_gen = require 'lqc.lqc_gen' +local tbl = require 'lqc.generators.table' +local int = require 'lqc.generators.int' +local float = require 'lqc.generators.float' +local str = require 'lqc.generators.string' +local bool = require 'lqc.generators.bool' + + +--- Creates a new generator that can generate a table, string, int, float or bool. +-- @param optional_samplesize Amount of times the property is tested, used to guide +-- the randomization process. +-- @return generator that can generate 1 of the previously mentioned types in +-- the description +local function new(optional_samplesize) + return lqc_gen.oneof { + tbl(optional_samplesize), + str(optional_samplesize), + int(optional_samplesize), + float(), + bool() + } +end + +return new + diff --git a/tools/lqc/generators/bool.lua b/tools/lqc/generators/bool.lua new file mode 100644 index 0000000..80238d5 --- /dev/null +++ b/tools/lqc/generators/bool.lua @@ -0,0 +1,25 @@ +--- Module for generating a bool randomly. +-- @classmod lqc.generators.bool +-- @alias new +local Gen = require 'lqc.generator' +local random = require 'lqc.random' + +--- Picks a random bool +-- @return true or false +local function pick() + return random.between(0, 1) == 0 +end + +--- Shrinks down a bool (always shrinks to false) +local function shrink(_) + return false +end + +--- Creates a new bool generator +-- @return A generator object for randomly generating bools. +local function new() + return Gen.new(pick, shrink) +end + +return new + diff --git a/tools/lqc/generators/byte.lua b/tools/lqc/generators/byte.lua new file mode 100644 index 0000000..4058996 --- /dev/null +++ b/tools/lqc/generators/byte.lua @@ -0,0 +1,14 @@ +--- Module for generating bytes randomly. +-- A byte is an integer with value between 0 - 255 (inclusive) +-- @classmod lqc.generators.byte +-- @alias byte +local int = require 'lqc.generators.int' + +-- Creates a new byte generator +-- @return generator for generating random byte values +local function byte() + return int(0, 255) +end + +return byte + diff --git a/tools/lqc/generators/char.lua b/tools/lqc/generators/char.lua new file mode 100644 index 0000000..a072160 --- /dev/null +++ b/tools/lqc/generators/char.lua @@ -0,0 +1,36 @@ +--- Module for generating a random (ASCII) char (no 'special' characters such as NUL, NAK, ...) +-- @lqc.generators.char +-- @alias char +local Gen = require 'lqc.generator' +local int = require 'lqc.generators.int' + +local lowest_ascii_value = 32 -- 'space' +local highest_ascii_value = 126 -- '~' +local int_gen = int(lowest_ascii_value, highest_ascii_value) +local space = string.char(lowest_ascii_value) + +-- Generates a random character (ASCII value between 'space' and '~' +-- @return randomly chosen char (string of length 1) +local function char_pick() + return string.char(int_gen:pick_func()) +end + +--- Shrinks down a previously generated char to a simpler value. Shrinks +-- towards the 'space' ASCII character. +-- @param prev previously generated char value +-- @return shrunk down char value +local function char_shrink(prev) + if string.byte(prev) <= lowest_ascii_value then + return space + end + return string.char(string.byte(prev) - 1) +end + +--- Creates a generator for ASCII-chars +-- @return generator that can randomly create ASCII values +local function char() + return Gen.new(char_pick, char_shrink) +end + +return char + diff --git a/tools/lqc/generators/float.lua b/tools/lqc/generators/float.lua new file mode 100644 index 0000000..be941cd --- /dev/null +++ b/tools/lqc/generators/float.lua @@ -0,0 +1,30 @@ +--- Module for generating float values. +-- @lqc.generators.float +-- @alias new +local Gen = require 'lqc.generator' + +-- Generates a random float. +-- @param numtests Number of times this generator is called in a test; used to +-- guide the randomization process. +-- @return random float (between - numtests / 2 and numtests / 2). +local function float_pick(numtests) + local lower_bound = -numtests / 2 + local upper_bound = numtests / 2 + return lower_bound + math.random() * (upper_bound - lower_bound) +end + +--- Shrinks a float to a simpler value +-- @param prev a previously generated float value +-- @return shrunk down float value +local function float_shrink(prev) + return prev / 2 +end + +--- Creates a generator for float values +-- @return a generator that can generate float values. +local function new() + return Gen.new(float_pick, float_shrink) +end + +return new + diff --git a/tools/lqc/generators/int.lua b/tools/lqc/generators/int.lua new file mode 100644 index 0000000..48d7783 --- /dev/null +++ b/tools/lqc/generators/int.lua @@ -0,0 +1,108 @@ +--- Module for generating integer values +-- @module lqc.generators.int +-- @alias new +local Gen = require 'lqc.generator' +local random = require 'lqc.random' +local abs = math.abs + +--- Helper function for picking a random integer, bounded by min and max. +-- @param min minimum value +-- @param max maximum value +-- @return function that can generate an integer (min <= int <= max) +local function pick_bounded(min, max) + local function do_pick() + return random.between(min, max) + end + return do_pick +end + +--- Helper function for finding number closest to 0. +-- @param a number 1 +-- @param b number 2 +-- @return number closest to 0 +local function find_closest_to_zero(a, b) + return (abs(a) < abs(b)) and a or b +end + +--- Helper function for shrinking integer, bounded by min and max. (min <= int <= max) +-- @param min minimum value +-- @param max maximum value +-- @return shrunk integer (shrinks towards 0 / closest value to 0 determined +-- by min and max) +local function shrink_bounded(min, max) + local bound_limit = find_closest_to_zero(min, max) + local function do_shrink(previous) + if previous == 0 or previous == bound_limit then + return previous + end + if previous > 0 then + return math.floor(previous / 2) + end + return math.ceil(previous / 2) + end + return do_shrink +end + +--- Picks a random integer, uniformy spread between +- sample_size / 2. +-- @param sample_size Number of times this generator is used in a property; +-- used to guide the optimatization process. +-- @return random integer +local function pick_uniform(sample_size) + local value = sample_size / 2 + return random.between(value - sample_size, value) +end + +--- Shrinks an integer by dividing it by 2 and rounding towards 0. +-- @param previous previously generated integer value +-- @return shrunk down integer value +local function shrink(previous) + if previous == 0 then + return 0 + end + if previous > 0 then + return math.floor(previous / 2) + end + return math.ceil(previous / 2) +end + +--- Creates a generator for generating an integer between min and max. +-- @param min minimum value +-- @param max maximum value +-- @return generator that generates integers between min and max. +local function integer_between(min, max) + return Gen.new(pick_bounded(min, max), shrink_bounded(min, max)) +end + +--- Creates a generator for generating a positive integer between 0 and max. +-- @param max maximum value +-- @return generator that generates integer between 0 and max. +local function positive_integer(max) + return Gen.new(pick_bounded(0, max), shrink) +end + +--- Creates a generator for generating an integer uniformly chosen +-- between +- sample_size / 2. +-- @return generator that can generate an integer +local function integer() + return Gen.new(pick_uniform, shrink) +end + +--- Creates a new integer generator. +-- @param nr1 number containing first bound +-- @param nr2 number containing second bound +-- @return generator that can generate integers according to the following strategy: +-- - nr1 and nr2 provided: nr1 <= int <= nr2 +-- - only nr1 provided: 0 <= int <= max +-- - no bounds provided: -numtests/2 <= int <= numtests/2 +local function new(nr1, nr2) + if nr1 and nr2 then + return integer_between(nr1, nr2) + end + if nr1 then + return positive_integer(nr1) + end + return integer() +end + +return new + diff --git a/tools/lqc/generators/string.lua b/tools/lqc/generators/string.lua new file mode 100644 index 0000000..01226d0 --- /dev/null +++ b/tools/lqc/generators/string.lua @@ -0,0 +1,200 @@ +--- Module for generating random strings +-- @module lqc.generators.string +-- @alias new +local random = require 'lqc.random' +local lqc = require 'lqc.quickcheck' +local Gen = require 'lqc.generator' +local char = require 'lqc.generators.char' +local char_gen = char() + +-- NOTE: The shrink algorithms are *heavily* based on triq +-- https://github.com/krestenkrab/triq + +--- Determines how many items to shrink. +-- @param length size of the string +-- @return integer indicating how many items to shrink +local function shrink_how_many(length) + -- 20% chance more than 1 member is shrunk + if random.between(1, 5) == 1 then + return random.between(1, length) + end + + return 1 +end + +--- Replaces a character in the string 'str' at index 'idx' with 'new_char'. +-- @param str string to be modified +-- @param new_char value that will replace the old character in the string +-- @param idx position in the string where the character should be replaced +-- @return updated string +local function string_replace_char(str, new_char, idx) + local result = {} + result[1] = string.sub(str, 1, idx - 1) + result[2] = new_char + result[3] = string.sub(str, idx + 1) + return table.concat(result) +end + +--- Replaces 1 character at a random location in the string, tries up to 100 +-- times if shrink gave back same result. +-- @param prev previously generated string value +-- @param length size of the string +-- @param iterations_count remaining tries to shrink down this string +-- @return shrunk down string value +local function do_shrink_generic(prev, length, iterations_count) + local idx = random.between(1, length) + local old_char = string.sub(prev, idx, idx) + local new_char = char_gen:shrink(old_char) + + if new_char == old_char and iterations_count ~= 0 then + -- Shrink introduced no simpler result, retry at other index. + return do_shrink_generic(prev, length, iterations_count - 1) + end + + return string_replace_char(prev, new_char, idx) +end + +--- Shrinks an amount of characters in the string. +-- @param str string to be shrunk down +-- @param length size of the string +-- @param how_many amount of characters to shrink +-- @return shrunk down string value +local function shrink_generic(str, length, how_many) + if how_many ~= 0 then + local new_str = do_shrink_generic(str, length, lqc.numshrinks) + return shrink_generic(new_str, length, how_many - 1) + end + + return str +end + +--- Determines if the string should be shrunk down to a shorter string +-- @param str_len size of the string +-- @return true: string should be made shorter; false: string should remain +-- size during shrinking +local function should_shrink_smaller(str_len) + if str_len == 0 then + return false + end + return random.between(1, 5) == 1 +end + +--- Shrinks the string by removing 1 character +-- @param str string to be shrunk down +-- @param str_len size of the string +-- @return new string with 1 random character removed +local function shrink_smaller(str, str_len) + local idx = random.between(1, str_len) + + -- Handle edge cases (first or last char) + if idx == 1 then + return string.sub(str, 2) + end + if idx == str_len then + return string.sub(str, 1, idx - 1) + end + + local new_str = {string.sub(str, 1, idx - 1), string.sub(str, idx + 1)} + return table.concat(new_str) +end + +--- Generates a string with a specific size. +-- @param size size of the string to generate +-- @return string of a specific size +local function do_generic_pick(size) + local result = {} + for _ = 1, size do + result[#result + 1] = char_gen:pick() + end + return table.concat(result) +end + +--- Generates a string with arbitrary length (0 <= size <= numtests). +-- @param numtests Number of times the property uses this generator; used to +-- guide the optimization process. +-- @return string of an arbitrary size +local function arbitrary_length_pick(numtests) + local size = random.between(0, numtests) + return do_generic_pick(size) +end + +--- Shrinks a string to a simpler form (smaller / different chars). +-- 1. Returns empty strings instantly +-- 2. Determine if string should be made shorter +-- 2.1 if true: remove a char +-- 2.2 otherwise: +-- * simplify a random amount of characters +-- * remove a char if simplify did not help +-- * otherwise return the simplified string +-- @param prev previously generated string value +-- @return shrunk down string value +local function arbitrary_length_shrink(prev) + local length = #prev + if length == 0 then + return prev + end -- handle empty strings + + if should_shrink_smaller(length) then + return shrink_smaller(prev, length) + end + + local new_str = shrink_generic(prev, length, shrink_how_many(length)) + if new_str == prev then + -- shrinking didn't help, remove an element + return shrink_smaller(new_str, length) + end + + -- string shrunk succesfully! + return new_str +end + +--- Helper function for generating a string with a specific size +-- @param size size of the string to generate +-- @return function that can generate strings of a specific size +local function specific_length_pick(size) + local function do_specific_pick() + return do_generic_pick(size) + end + return do_specific_pick +end + +--- Shrinks a string to a simpler form (only different chars since length is fixed). +-- * "" -> "" +-- * non-empty string, shrinks upto max 5 chars of the string +-- @param prev previously generated string value +-- @return shrunk down string value +local function specific_length_shrink(prev) + local length = #prev + if length == 0 then + return prev + end -- handle empty strings + return shrink_generic(prev, length, shrink_how_many(length)) +end + +--- Generator for a string with an arbitrary size +-- @return generator for a string of arbitrary size +local function arbitrary_length_string() + return Gen.new(arbitrary_length_pick, arbitrary_length_shrink) +end + +--- Creates a generator for a string of a specific size +-- @param size size of the string to generate +-- @return generator for a string of a specific size +local function specific_length_string(size) + return Gen.new(specific_length_pick(size), specific_length_shrink) +end + +--- Creates a new ASCII string generator +-- @param size size of the string +-- @return generator that can generate the following: +-- 1. size provided: a string of a specific size +-- 2. no size provided: string of an arbitrary size +local function new(size) + if size then + return specific_length_string(size) + end + return arbitrary_length_string() +end + +return new + diff --git a/tools/lqc/generators/table.lua b/tools/lqc/generators/table.lua new file mode 100644 index 0000000..b85d418 --- /dev/null +++ b/tools/lqc/generators/table.lua @@ -0,0 +1,184 @@ +--- Module for generating tables of varying sizes and types +-- @module lqc.generators.table +-- @alias new_table +local Gen = require 'lqc.generator' +local random = require 'lqc.random' +local bool = require 'lqc.generators.bool' +local int = require 'lqc.generators.int' +local float = require 'lqc.generators.float' +local string = require 'lqc.generators.string' +local lqc = require 'lqc.quickcheck' +local lqc_gen = require 'lqc.lqc_gen' +local oneof = lqc_gen.oneof +local frequency = lqc_gen.frequency +local deep_equals = require 'lqc.helpers.deep_equals' +local deep_copy = require 'lqc.helpers.deep_copy' + +--- Checks if an object is equal to another (shallow equals) +-- @param a first value +-- @param b second value +-- @return true if a equals b; otherwise false +local function normal_equals(a, b) + return a == b +end + +--- Determines if the shrink mechanism should try to reduce the table in size. +-- @param size size of the table to shrink down +-- @return true if size should be reduced; otherwise false +local function should_shrink_smaller(size) + if size == 0 then + return false + end + return random.between(1, 5) == 1 +end + +--- Determines how many items in the table should be shrunk down +-- @param size size of the table +-- @return amount of values in the table that should be shrunk down +local function shrink_how_many(size) + -- 20% chance between 1 and size, 80% 1 element. + if random.between(1, 5) == 1 then + return random.between(1, size) + end + return 1 +end + +--- Creates a generator for a table of arbitrary or specific size +-- @param table_size size of the table to be generated +-- @return generator that can generate tables +local function new_table(table_size) + -- Keep a list of generators used in this new table + -- This variable is needed to share state (which generators) between + -- the pick and shrink function + local generators = {} + + --- Shrinks the table by 1 randomly chosen element + -- @param tbl previously generated table value + -- @param size size of the table + -- @return shrunk down table + local function shrink_smaller(tbl, size) + local idx = random.between(1, size) + table.remove(tbl, idx) + table.remove(generators, idx) -- also update the generators for this table!! + return tbl + end + + --- Shrink a value in the table. + -- @param tbl table to shrink down + -- @param size size of the table + -- @param iterations_count remaining amount of times the shrinking should be retried + -- @return shrunk down table + local function do_shrink_values(tbl, size, iterations_count) + local idx = random.between(1, size) + local old_value = tbl[idx] + local new_value = generators[idx]:shrink(old_value) + + -- Check if we should retry shrinking: + if iterations_count ~= 0 then + local check_equality = (type(new_value) == 'table') and deep_equals or normal_equals + if check_equality(new_value, old_value) then + -- Shrink introduced no simpler result, retry at other index. + return do_shrink_values(tbl, size, iterations_count - 1) + end + end + + tbl[idx] = new_value + return tbl + end + + --- Shrinks an amount of values in the table + -- @param tbl table to shrink down + -- @param size size of the table + -- @param how_many amount of values to shrink down + -- @return shrunk down table + local function shrink_values(tbl, size, how_many) + if how_many ~= 0 then + local new_tbl = do_shrink_values(tbl, size, lqc.numshrinks) + return shrink_values(new_tbl, size, how_many - 1) + end + return tbl + end + + --- Generates a random table with a certain size + -- @param size size of the table to generate + -- @return tableof a specific size with random values + local function do_generic_pick(size) + local result = {} + for idx = 1, size do + -- TODO: Figure out a better way to decrease table size rapidly, maybe use + -- math.log (e.g. ln(size + 1) ? + local subtable_size = math.floor(size * 0.01) + local generator = frequency {{10, new_table(subtable_size)}, + {90, oneof {bool(), int(size), float(), string(size)}}} + generators[idx] = generator + result[idx] = generator:pick(size) + end + return result + end + + -- Now actually generate a table: + if table_size then -- Specific size + --- Helper function for generating a table of a specific size + -- @param size size of the table to generate + -- @return function that can generate a table of a specific size + local function specific_size_pick(size) + local function do_pick() + return do_generic_pick(size) + end + return do_pick + end + + --- Shrinks a table without removing any elements. + -- @param prev previously generated table value + -- @return shrunk down table + local function specific_size_shrink(prev) + local size = #prev + if size == 0 then + return prev + end -- handle empty tables + return shrink_values(prev, size, shrink_how_many(size)) + end + + return Gen.new(specific_size_pick(table_size), specific_size_shrink) + end + + -- Arbitrary size + + --- Generate a (nested / empty) table of an arbitrary size. + -- @param numtests Amount of times the property calls this generator; used to + -- guide the optimatization process. + -- @return table of an arbitrary size + local function arbitrary_size_pick(numtests) + local size = random.between(0, numtests) + return do_generic_pick(size) + end + + --- Shrinks a table by removing elements or shrinking values in the table. + -- @param prev previously generated value + -- @return shrunk down table value + local function arbitrary_size_shrink(prev) + local size = #prev + if size == 0 then + return prev + end -- handle empty tables + + if should_shrink_smaller(size) then + return shrink_smaller(prev, size) + end + + local tbl_copy = deep_copy(prev) + local new_tbl = shrink_values(tbl_copy, size, shrink_how_many(size)) + if deep_equals(prev, new_tbl) then + -- shrinking didn't help, remove an element + return shrink_smaller(prev, size) + end + + -- table shrunk successfully! + return new_tbl + end + + return Gen.new(arbitrary_size_pick, arbitrary_size_shrink) +end + +return new_table + diff --git a/tools/lqc/helpers/deep_copy.lua b/tools/lqc/helpers/deep_copy.lua new file mode 100644 index 0000000..c884336 --- /dev/null +++ b/tools/lqc/helpers/deep_copy.lua @@ -0,0 +1,39 @@ +--- Helper module for performing a deep copy. +-- @module lqc.helpers.deep_copy +-- @alias deep_copy +local pairs = pairs +local type = type +local setmetatable = setmetatable +local getmetatable = getmetatable + +--- Deep copies an object recursively (including (nested) tables, metatables, +-- circular references, ...) +-- Heavily based on http://stackoverflow.com/questions/640642/how-do-you-copy-a-lua-table-by-value +-- @param obj Object to be copied +-- @param seen Table of previously seen objects (for handling circular references), default nil +-- @return deep copy of obj +local function deep_copy(obj, seen) + -- handle number, string, boolean, ... + if type(obj) ~= 'table' then + return obj + end + + seen = seen or {} + if seen[obj] then + return seen[obj] + end -- handle circular references + + -- handle table + local result = {} + seen[obj] = result + + for key, value in pairs(obj) do + result[deep_copy(key, seen)] = deep_copy(value, seen) + end + + -- handle metatable + return setmetatable(result, deep_copy(getmetatable(obj), seen)) +end + +return deep_copy + diff --git a/tools/lqc/helpers/deep_equals.lua b/tools/lqc/helpers/deep_equals.lua new file mode 100644 index 0000000..1d28421 --- /dev/null +++ b/tools/lqc/helpers/deep_equals.lua @@ -0,0 +1,37 @@ +--- Helper module for checking if 2 values are equal by value. +-- @module lqc.helpers.deep_equals +-- @alias deep_equals +local pairs = pairs +local type = type + +--- Checks 1 value is equal to another. Also works for nested structures. +-- @param a value a +-- @param b value b +-- @return true if objects are equal; otherwise false +local function deep_equals(a, b) + local type_a = type(a) + if type_a ~= type(b) then + return false + end + if type_a ~= 'table' then + return a == b + end + + if #a ~= #b then + return false + end + for k, v1 in pairs(a) do + local v2 = b[k] + if type(v1) == 'table' then + return deep_equals(v1, v2) + end + if v1 ~= v2 then + return false + end + end + + return true +end + +return deep_equals + diff --git a/tools/lqc/helpers/filter.lua b/tools/lqc/helpers/filter.lua new file mode 100644 index 0000000..a2c0f0c --- /dev/null +++ b/tools/lqc/helpers/filter.lua @@ -0,0 +1,23 @@ +--- Helper module for filtering elements out of an array based on a predicate. +-- @module lqc.helpers.filter +-- @alias filter +--- Filters an array based on a predicate function +-- @param array List of values in a table +-- @param predicate Function taking 1 argument (element in the array), returns +-- a bool indicating if element should be removed or not +-- @return new array containing only the values for which the predicate is true +local function filter(array, predicate) + local result = {} + + for idx = 1, #array do + local value = array[idx] + if predicate(value) then + result[#result + 1] = value + end + end + + return result +end + +return filter + diff --git a/tools/lqc/helpers/fs.lua b/tools/lqc/helpers/fs.lua new file mode 100644 index 0000000..e217ea1 --- /dev/null +++ b/tools/lqc/helpers/fs.lua @@ -0,0 +1,109 @@ +--- Helper module for everything filesystem related. +-- @module lqc.helpers.fs +-- @alias lib +local lfs = require 'lfs' +local Vector = require 'lqc.helpers.vector' + +local lib = {} + +--- Concatenates multiple strings together into 1 big string +-- @param strings array of strings to be concatenated together +-- @return the concatenated string +local function strcat(...) + return table.concat({...}) +end + +--- Checks if 'f' is a file? +-- @param f string of a file path +-- @return true if f is a file; otherwise false +function lib.is_file(f) + return lfs.attributes(f, 'mode') == 'file' +end + +--- Check if 'd' is a directory? +-- @param d string of a directory path +-- @return true if d is a directory; otherwise false +function lib.is_dir(d) + return lfs.attributes(d, 'mode') == 'directory' +end + +--- Is the file a Lua file? (=a file ending in .lua) +-- @param file path to a file +-- @return true if it is a Lua file; otherwise false. +function lib.is_lua_file(file) + return file:match('%.lua$') ~= nil +end + +--- Is the file a Moonscript file? (= a file ending in .moon) +-- @param file path to a file +-- @return true if it is a Moonscript file; otherwise false. +function lib.is_moonscript_file(file) + return file:match('%.moon$') ~= nil +end + +--- Removes a file from the filesystem. +-- @param path Path to the file that should be removed +function lib.remove_file(path) + os.remove(path) +end + +--- Checks if a file exists. +-- @param path path to a file +-- @return true if the file does exist; otherwise false +function lib.file_exists(path) + return lfs.attributes(path) ~= nil +end + +--- Reads the entire contents from a (binary) file and returns it. +-- @param path path to the file to read from +-- @return the contents of the file as a string or nil on error +function lib.read_file(path) + local file = io.open(path, 'rb') + if not file then + return nil + end + local contents = file:read '*a' + file:close() + return contents +end + +--- Writes the 'new_contents' to the (binary) file specified by 'path' +-- @param path path to file the contents should be written to +-- @param new_contents the contents that will be written +-- @return nil; raises an error if file could not be opened. +function lib.write_file(path, new_contents) + if not new_contents then + return + end + local file = io.open(path, 'wb') + if not file then + error('Could not write to ' .. path .. '!') + end + file:write(new_contents) + file:close() +end + +--- Finds all files in a directory. +-- @param directory_path String of a directory path +-- @return a table containing all files in this directory and it's +-- subdirectories. Raises an error if dir is not a valid +-- string to a directory path. +function lib.find_files(directory_path) + local result = Vector.new() + + for file_name in lfs.dir(directory_path) do + if file_name ~= '.' and file_name ~= '..' then + local file = strcat(directory_path, '/', file_name) + if lib.is_dir(file) then + result:append(Vector.new(lib.find_files(file))) + elseif lib.is_file(file) then + result:push_back(file) + end + end + end + + return result:to_table() +end + +return lib + diff --git a/tools/lqc/helpers/map.lua b/tools/lqc/helpers/map.lua new file mode 100644 index 0000000..0ac14f9 --- /dev/null +++ b/tools/lqc/helpers/map.lua @@ -0,0 +1,19 @@ +--- Helper moduile for performing a function on each element in an array. +-- @module lqc.helpers.map +-- @alias map +--- Maps a function over an array +-- @param array List of elements on which a function will be applied +-- @param func Function to be applied over the array. Takes 1 argument (element of the array); returns a result +-- @return A new array with func applied to each element in the array +local function map(array, func) + local result = {} + + for idx = 1, #array do + result[#result + 1] = func(array[idx]) + end + + return result +end + +return map + diff --git a/tools/lqc/helpers/reduce.lua b/tools/lqc/helpers/reduce.lua new file mode 100644 index 0000000..d3e52e6 --- /dev/null +++ b/tools/lqc/helpers/reduce.lua @@ -0,0 +1,33 @@ +--- Helper module for reducing an array of values into a single value. +-- @module lqc.helpers.reduce +-- @alias reeduce +--- Helper function that performs the actual reduce operation +-- @param array List of elements to be reduced into 1 value +-- @param acc Accumulator containing the temporary result +-- @param func Function to be applied for each element in the array. Takes 2 +-- arguments: element out of the array and the current state of the +-- accumulator. Returns the updated accumulator state +-- @param pos Position in the array to apply the reduce operation on +-- @return the result obtained by reducing the array into 1 value +local function do_reduce(array, acc, func, pos) + if pos < #array then + local new_pos = pos + 1 + return do_reduce(array, func(array[new_pos], acc), func, new_pos) + end + + return acc +end + +--- Reduces an array of values into a single value +-- @param array List of elements to be reduced into 1 value +-- @param start Start value of the accumulator +-- @param func Function to be applied for each element in the array. Takes 2 +-- arguments: element out of the array and the current state of the +-- accumulator. Returns the updated accumulator state +-- @return the result obtained by reducing the array into 1 value +local function reduce(array, start, func) + return do_reduce(array, start, func, 0) +end + +return reduce + diff --git a/tools/lqc/helpers/vector.lua b/tools/lqc/helpers/vector.lua new file mode 100644 index 0000000..c06394f --- /dev/null +++ b/tools/lqc/helpers/vector.lua @@ -0,0 +1,115 @@ +--- Module for a data container that does not allow nil values. +-- @classmod lqc.helpers.vector +-- @alias Vector +local deep_equals = require 'lqc.helpers.deep_equals' + +local Vector = {} +local Vector_mt = { + __index = Vector, +} + +--- Constructs a new vector, possibly filled with data (a table value) +-- @param data[opt={}] the data to be stored in the vector initially +-- @return a new vector filled with the initial data if provided. +function Vector.new(data) + local begin_data = data or {} + local vector = { + data = begin_data, + } + return setmetatable(vector, Vector_mt) +end + +--- Adds an element to the back of the vector. +-- @param obj a non-nil value +-- @return self (for method chaining); raises an error if trying to add nil to the vector +function Vector:push_back(obj) + if obj == nil then + error 'nil is not allowed in vector datastructure!' + end + table.insert(self.data, obj) + return self +end + +--- Replaces an element in the vector. +-- @param idx Index of the element in the vector to be replaced +-- @param obj Object that the previous object should be replaced with +-- @return self (for method chaining); raises an error if idx is an index +-- not present in the vector +function Vector:replace(idx, obj) + local vec_size = self:size() + if idx < 1 or idx > vec_size then + error('Invalid index! Index should be between 1 and ' .. vec_size) + end + self.data[idx] = obj + return self +end + +--- Appends another vector to this vector +-- @param other_vec Another vector object +-- @return a vector containing the data of both vectors +function Vector:append(other_vec) + for i = 1, other_vec:size() do + self:push_back(other_vec:get(i)) + end + return self +end + +--- Gets the element at position 'index' in the vector +-- @param index position of the value in the vector +-- @return element at position 'index +function Vector:get(index) + return self.data[index] +end + +--- Checks if an element is contained in the vector +-- @param element element to be checked if it is in the vector +-- @return true if the element is already in the vector; otherwise false. +function Vector:contains(element) + for i = 1, #self.data do + if self.data[i] == element then + return true + end + end + return false +end + +--- Returns the size of the vector. +-- @return length of the vector (0 if empty) +function Vector:size() + return #self.data +end + +--- Removes an element from the vector by value +-- @param obj object to remove +function Vector:remove(obj) + -- Find element, then remove by index + local pos = -1 + for i = 1, #self.data do + if deep_equals(self.data[i], obj) then + pos = i + break + end + end + if pos == -1 then + return + end + table.remove(self.data, pos) +end + +--- Removes an element from the vector by index +-- @param idx Position of the element you want to remove +function Vector:remove_index(idx) + if idx > self:size() then + return + end + table.remove(self.data, idx) +end + +--- Returns the vector, with the contents represented as a flat table +-- @return Table with the contents of the vector +function Vector:to_table() + return self.data +end + +return Vector + diff --git a/tools/lqc/lqc_gen.lua b/tools/lqc/lqc_gen.lua new file mode 100644 index 0000000..78c7133 --- /dev/null +++ b/tools/lqc/lqc_gen.lua @@ -0,0 +1,123 @@ +--- Helper module providing various generators for generating data. +-- @module lqc.lqc_gen +-- @alias lib +local Gen = require 'lqc.generator' +local random = require 'lqc.random' +local reduce = require 'lqc.helpers.reduce' + +local lib = {} + +--- Picks a number randomly between min and max. +-- @param min Minimum value to pick from +-- @param max Maximum value to pick from +-- @return a random value between min and max +local function choose_pick(min, max) + local function pick() + return random.between(min, max) + end + return pick +end + +--- Shrinks a value between min and max by dividing the sum of the closest +-- number to 0 and the generated value with 2. +-- This effectively reduces it to the value closest to 0 gradually in the +-- chosen range. +-- @param min Minimum value to pick from +-- @param max Maximum value to pick from +-- @return a shrunk value between min and max +local function choose_shrink(min, max) + local shrink_to = (math.abs(min) < math.abs(max)) and min or max + + local function shrink(value) + local shrunk_value = (shrink_to + value) / 2 + + if shrunk_value < 0 then + return math.ceil(shrunk_value) + else + return math.floor(shrunk_value) + end + end + + return shrink +end + +--- Creates a generator, chooses an integer between min and max (inclusive range). +-- @param min Minimum value to pick from +-- @param max Maximum value to pick from +-- @return a random value between min and max +function lib.choose(min, max) + return Gen.new(choose_pick(min, max), choose_shrink(min, max)) +end + +--- Select a generator from a list of generators +-- @param generators Table containing an array of generator objects. +-- @return A new generator that randomly uses 1 of the generators in the list. +function lib.oneof(generators) + local which + local function oneof_pick(numtests) + which = random.between(1, #generators) + return generators[which]:pick(numtests) + end + local function oneof_shrink(prev) + return generators[which]:shrink(prev) + end + + return Gen.new(oneof_pick, oneof_shrink) +end + +--- Select a generator from a list of weighted generators ({{weight1, gen1}, ... }) +-- @param generators A table containing an array of weighted generators. +-- @return A new generator that randomly uses a generator from the list, taking the +-- weights into account. +function lib.frequency(generators) + local which + + local function do_sum(generator, acc) + return generator[1] + acc + end + local function frequency_pick(numtests) + local sum = reduce(generators, 0, do_sum) + + local val = random.between(1, sum) + which = reduce(generators, {0, 1}, function(generator, acc) + local current_sum = acc[1] + generator[1] + if current_sum >= val then + return acc + else + return {current_sum, acc[2] + 1} + end + end)[2] + + return generators[which][2]:pick(numtests) + end + local function frequency_shrink(prev) + return generators[which][2]:shrink(prev) + end + + return Gen.new(frequency_pick, frequency_shrink) +end + +--- Create a generator that selects an element based on the input list. +-- @param array an array of constant values +-- @return Generator that can pick 1 of the values in the array, shrinks +-- towards beginning of the list. +function lib.elements(array) + local last_idx + local function elements_pick() + local idx = random.between(1, #array) + last_idx = idx + return array[idx] + end + + local function elements_shrink(_) + if last_idx > 1 then + last_idx = last_idx - 1 + end + return array[last_idx] + end + + return Gen.new(elements_pick, elements_shrink) +end + +return lib + diff --git a/tools/lqc/property.lua b/tools/lqc/property.lua new file mode 100644 index 0000000..43ca4f2 --- /dev/null +++ b/tools/lqc/property.lua @@ -0,0 +1,228 @@ +--- Module for creating properties. Provides a small domain specific language +-- for ease of use. +-- @module lqc.property +-- @alias property +local lqc = require 'lqc.quickcheck' +local report = require 'lqc.report' +local results = require 'lqc.property_result' +local unpack = unpack or table.unpack -- for compatibility reasons + + +--- Helper function, checks if x is an integer. +-- @param x a value to be checked +-- @return true if x is an integer; false otherwise. +local function is_integer(x) + return type(x) == 'number' and x % 1 == 0 +end + +--- Adds a small wrapper around the check function indicating success or failure +-- @param prop_table Property to be wrapped. +local function add_check_wrapper(prop_table) + local check_func = prop_table.check + prop_table.check = function(...) + if check_func(...) then + return results.SUCCESS + else + return results.FAILURE + end + end +end + +--- Adds an 'implies' wrapper to the check function +-- @param prop_table Property to be wrapped. +local function add_implies(prop_table) + local check_func = prop_table.check + prop_table.check = function(...) + if prop_table.implies(...) == false then + return results.SKIPPED + end + + return check_func(...) + end +end + +--- Adds a 'when_fail' wrapper to the check function +-- @param prop_table Property to be wrapped. +local function add_when_fail(prop_table) + local check_func = prop_table.check + prop_table.check = function(...) + local result = check_func(...) + + if result == results.FAILURE then + prop_table.when_fail(...) + end + + return result + end +end + +--- Shrinks a property that failed with a certain set of inputs. +-- This function is called recursively if a shrink fails. +-- This function returns a simplified list or inputs. +-- @param property the property that failed +-- @param generated_values array of values to be shrunk down +-- @param tries Amount of shrinking tries already done +-- @return array of shrunk down values +local function do_shrink(property, generated_values, tries) + if not tries then + tries = 1 + end + local shrunk_values = property.shrink(unpack(generated_values)) + local result = property(unpack(shrunk_values)) + + if tries == property.numshrinks then + -- Maximum amount of shrink attempts exceeded. + return generated_values + end + + if result == results.FAILURE then + -- further try to shrink down + return do_shrink(property, shrunk_values, tries + 1) + elseif result == results.SKIPPED then + -- shrunk to invalid situation, retry + return do_shrink(property, generated_values, tries + 1) + end + + -- return generated values since they were last values for which property failed! + return generated_values +end + +--- Function that checks if the property is valid for a set amount of inputs. +-- 1. check result of property X amount of times: +-- - SUCCESS = OK, print '.' +-- - SKIPPED = OK, print 'x' +-- - FAILURE = NOT OK, see 2. +-- 2. if FAILURE: +-- 2.1 print property info, values for which it fails +-- 2.2 do shrink to find minimal error case +-- 2.3 when shrink stays the same or max amount exceeded -> print minimal example +-- @param property Property to be checked X amount of times. +-- @return nil on success; otherwise returns a table containing error info. +local function do_check(property) + for _ = 1, property.numtests do + local generated_values = property.pick() + local result = property(unpack(generated_values)) + + if result == results.SUCCESS then + report.report_success() + elseif result == results.SKIPPED then + report.report_skipped() + else + report.report_failed() + if #generated_values == 0 then + -- Empty list of generators -> no further shrinking possible! + return { + property = property, + generated_values = generated_values, + shrunk_values = generated_values, + } + end + + local shrunk_values = do_shrink(property, generated_values) + return { + property = property, + generated_values = generated_values, + shrunk_values = shrunk_values, + } + end + end + + return nil +end + +--- Creates a new property. +-- NOTE: property is limited to 1 implies, for_all, when_fail +-- more complex scenarios should be handled with state machine. +-- @param descr String containing description of the property +-- @param property_func Function to be checked X amount of times +-- @param generators List of generators used to generate values supplied to 'property_func' +-- @param numtests Number of times the property should be checked +-- @param numshrinks Number of times a failing property should be shrunk down +-- @return property object +local function new(descr, property_func, generators, numtests, numshrinks) + local prop = { + description = descr, + numtests = numtests, + numshrinks = numshrinks, + } + + -- Generates a new set of inputs for this property. + -- Returns the newly generated set of inputs as a table. + function prop.pick() + local generated_values = {} + for i = 1, #generators do + generated_values[i] = generators[i]:pick(numtests) + end + return generated_values + end + + -- Shrink 1 value randomly out of the given list of values. + function prop.shrink(...) + local values = {...} + local which = math.random(#values) + local shrunk_value = generators[which]:shrink(values[which]) + values[which] = shrunk_value + return values + end + + -- Function that checks if the property is valid for a set amount of inputs. + function prop:check() + return do_check(self) + end + + return setmetatable(prop, { + __call = function(_, ...) + return property_func(...) + end, + }) +end + +--- Inserts the property into the list of existing properties. +-- @param descr String containing text description of the property +-- @param prop_info_table Table containing information of the property +-- @return nil; raises an error if prop_info_table is not in a valid format +local function property(descr, prop_info_table) + local function prop_func(prop_table) + local generators = prop_table.generators + if not generators or type(generators) ~= 'table' then + error('Need to supply generators in property!') + end + + local check_type = type(prop_table.check) + if check_type ~= 'function' and check_type ~= 'table' then + error('Need to provide a check function to property!') + end + + add_check_wrapper(prop_table) + + local implies_type = type(prop_table.implies) + if implies_type == 'function' or implies_type == 'table' then + add_implies(prop_table) + end + + local when_fail_type = type(prop_table.when_fail) + if when_fail_type == 'function' or when_fail_type == 'table' then + add_when_fail(prop_table) + end + + local it_amount = prop_table.numtests + local shrink_amount = prop_table.numshrinks + local numtests = is_integer(it_amount) and it_amount or lqc.numtests + local numshrinks = is_integer(shrink_amount) and shrink_amount or lqc.numshrinks + local new_prop = new(descr, prop_table.check, prop_table.generators, numtests, numshrinks) + table.insert(lqc.properties, new_prop) + end + + -- property called without DSL-like syntax + if prop_info_table then + prop_func(prop_info_table) + return function() + end + end + + -- property called with DSL syntax! + return prop_func +end + +return property + diff --git a/tools/lqc/property_result.lua b/tools/lqc/property_result.lua new file mode 100644 index 0000000..77086ab --- /dev/null +++ b/tools/lqc/property_result.lua @@ -0,0 +1,13 @@ +--- Helper module containing enumeration of all possible results after +-- evaluating a property. +-- @module lqc.property_result +--- List of possible results after executing property +-- @table result_enum +-- @field SUCCESS property succeeded +-- @field FAILURE property failed +-- @field SKIPPED property skipped (implies predicate not met) +return { + SUCCESS = 1, + FAILURE = 2, + SKIPPED = 3, +} \ No newline at end of file diff --git a/tools/lqc/quickcheck.lua b/tools/lqc/quickcheck.lua new file mode 100644 index 0000000..7339ba1 --- /dev/null +++ b/tools/lqc/quickcheck.lua @@ -0,0 +1,81 @@ +--- Module which contains the core of the quickcheck engine. +-- @module lqc.quickcheck +-- @alias lib +local report = require 'lqc.report' +local map = require 'lqc.helpers.map' + +local shuffle = pairs +local lib = { + properties = {}, -- list of all properties + numtests = nil, -- Default amount of times a property should be tested + numshrinks = nil, -- Default amount of times a failing property should be shrunk down + failed = false, +} + +--- Checks if the quickcheck configuration is initialized +-- @return true if it is initialized; otherwise false. +local function is_initialized() + return lib.numtests ~= nil and lib.numshrinks ~= nil +end + +--- Handles the result of a property. +-- @param result table containing information of the property (or nil on success) +-- @see lqc.property_result +local function handle_result(result) + if not result then + return + end -- successful + lib.failed = true + if type(result.property) == 'table' then -- property failed + report.report_failed_property(result.property, result.generated_values, result.shrunk_values) + return + end + + -- FSM failed + report.report_failed_fsm(result.description, result.generated_values, result.shrunk_values) +end + +--- Configures the amount of iterations and shrinks the check algorithm should perform. +-- @param numtests Default number of tests per property +-- @param numshrinks Default number of shrinks per property +function lib.init(numtests, numshrinks) + lib.numtests = numtests + lib.numshrinks = numshrinks +end + +--- Iterates over all properties in a random order and checks if the property +-- holds true for each generated set of inputs. Raises an error if quickcheck +-- engine is not initialized yet. +function lib.check() + if not is_initialized() then + error 'quickcheck.init() has to be called before quickcheck.check()!' + end + + for _, prop in shuffle(lib.properties) do + local result = prop:check() + handle_result(result) + end +end + +--- Multithreaded version of check(), uses a thread pool in the underlying +-- implementation. Splits up the properties over different threads. +-- @param numthreads Number of the threads to divide the properties over. +function lib.check_mt(numthreads) + local ThreadPool = require 'lqc.threading.thread_pool' + + if not is_initialized() then + error 'quickcheck.init() has to be called before quickcheck.check_mt()!' + end + + local pool = ThreadPool.new(numthreads) + for _, prop in shuffle(lib.properties) do + pool:schedule(function() + return prop:check() + end) + end + local results = pool:join() + map(results, handle_result) +end + +return lib + diff --git a/tools/lqc/random.lua b/tools/lqc/random.lua new file mode 100644 index 0000000..90745cd --- /dev/null +++ b/tools/lqc/random.lua @@ -0,0 +1,29 @@ +--- Helper module for generating random numbers. +-- @module lqc.random +-- @alias lib +local time = os.time +local random_seed = math.randomseed +local random = math.random + +local lib = {} + +--- Seeds the random number generator +-- @param seed Random seed (number) or nil for current timestamp +-- @return The random seed used to initialize the random number generator with. +function lib.seed(seed) + if not seed then + seed = time() + end + random_seed(seed) + return seed +end + +--- Get random number between min and max +-- @param min Minimum value to generate a random number in +-- @param max Maximum value to generate a random number in (inclusive) +function lib.between(min, max) + return random(min, max) +end + +return lib + diff --git a/tools/lqc/report.lua b/tools/lqc/report.lua new file mode 100644 index 0000000..c800385 --- /dev/null +++ b/tools/lqc/report.lua @@ -0,0 +1,126 @@ +--- Helper module for reporting test results to the user. +-- @module lqc.report +-- @alias lib +local map = require 'lqc.helpers.map' + +local write = io.write +local ipairs = ipairs + +-- Variables for reporting statistics after test run is over. +local passed_amount = 0 +local failed_amount = 0 +local skipped_amount = 0 +local reported_errors = {} + +local lib = {} + +--- Formats a table to a human readable string +-- @param t table to be formatted +-- @return formatted table (as a string) +local function format_table(t) + local result = '{ ' + for _, v in ipairs(t) do + local type_v = type(v) + if type_v == 'table' then + result = result .. format_table(v) .. ' ' + elseif type_v == 'boolean' then + result = result .. (v and 'true ' or 'false ') + else + result = result .. v .. ' ' + end + end + return result .. '}' +end + +--- Writes a string to stdout (no newline at end). +-- @param s string to be written to stdout +function lib.report(s) + write(s) +end + +--- Prints the used random seed to stdout. +-- @param seed Random seed to be printed to stdout +function lib.report_seed(seed) + lib.report('Random seed = ' .. seed .. '\n') +end + +--- Prints a '.' to stdout +function lib.report_success() + passed_amount = passed_amount + 1 + lib.report '.' +end + +--- Prints a green '.' to stdout +local function report_success_colored() + passed_amount = passed_amount + 1 + lib.report '\27[32m.\27[0m' +end + +--- Prints a 'x' to stdout +function lib.report_skipped() + skipped_amount = skipped_amount + 1 + lib.report 'x' +end + +--- Prints a yellow 'x' to stdout +local function report_skipped_colored() + skipped_amount = skipped_amount + 1 + lib.report '\27[33mx\27[0m' +end + +--- Prints an 'F' to stdout +function lib.report_failed() + failed_amount = failed_amount + 1 + lib.report 'F' +end + +--- Prints a red 'F' to stdout +local function report_failed_colored() + failed_amount = failed_amount + 1 + lib.report '\27[31mF\27[0m' +end + +--- Saves an error to the list of errors. +function lib.save_error(failure_str) + table.insert(reported_errors, failure_str) +end + +--- Prints out information regarding the failed property +function lib.report_failed_property(property, generated_values, shrunk_values) + lib.save_error('\nProperty "' .. property.description .. '" failed!\n' .. 'Generated values = ' .. + format_table(generated_values) .. '\n' .. 'Simplified solution to = ' .. + format_table(shrunk_values) .. '\n') +end + +--- Prints out information regarding the failed FSM. +function lib.report_failed_fsm(description) + -- TODO output more information + lib.save_error('\nFSM ' .. description .. ' failed!\n') +end + +--- Reports all errors to stdout. +function lib.report_errors() + map(reported_errors, lib.report) + lib.report '\n' -- extra newline as separator between errors +end + +--- Prints a summary about certain statistics (test passed / failed, ...) +function lib.report_summary() + local total_tests = passed_amount + failed_amount + skipped_amount + lib.report('' .. total_tests .. ' tests, ' .. failed_amount .. ' failures, ' .. skipped_amount .. ' skipped.\n') +end + +--- Configures this module to use ANSI colors when printing to terminal or not. +-- @param enable_colors true: colors will be used when printing to terminal; +-- otherwise plain text will be printed. +function lib.configure(enable_colors) + if not enable_colors then + return + end + lib.report_success = report_success_colored + lib.report_skipped = report_skipped_colored + lib.report_failed = report_failed_colored +end + +return lib + diff --git a/tools/lqc/threading/msg_processor.lua b/tools/lqc/threading/msg_processor.lua new file mode 100644 index 0000000..8134c6e --- /dev/null +++ b/tools/lqc/threading/msg_processor.lua @@ -0,0 +1,44 @@ +--- Helper module for a message processor that can process incoming messages +-- from other threads. +-- @classmod lqc.threading.msg_processor +-- @alias MsgProcessor +--- Checks if 'x' is callable. +-- @param x value to be checked +-- @return true if callable; otherwise false. +local function is_callable(x) + local type_x = type(x) + return type_x == 'function' or type_x == 'table' +end + +local MsgProcessor = { + TASK_TAG = 'task', + RESULT_TAG = 'result', + STOP_VALUE = 'stop', + VOID_RESULT = '__VOID', +} + +--- Creates an object that can handle incoming messages. +-- @param msg_box An object that can be used to send and receive incoming messages with +-- @return a new MsgProcessor object +function MsgProcessor.new(msg_box) + local function main_loop_msg_processor() + -- TODO init random seed per thread? + while true do + local _, cmd = msg_box:receive(nil, MsgProcessor.TASK_TAG) + if cmd == MsgProcessor.STOP_VALUE then + return + elseif is_callable(cmd) then + -- NOTE: threadpool hangs if it returns nil.. + local result = cmd() or MsgProcessor.VOID_RESULT + msg_box:send(nil, MsgProcessor.RESULT_TAG, result) + else + return + end + end + end + + return main_loop_msg_processor +end + +return MsgProcessor + diff --git a/tools/lqc/threading/thread_pool.lua b/tools/lqc/threading/thread_pool.lua new file mode 100644 index 0000000..f085e6c --- /dev/null +++ b/tools/lqc/threading/thread_pool.lua @@ -0,0 +1,83 @@ +--- Module for creating a thread pool, based on Lua Lanes. +-- @module lqc.threading.thread_pool +-- @alias ThreadPool +local MsgProcessor = require 'lqc.threading.msg_processor' +local map = require 'lqc.helpers.map' +local lanes = require('lanes').configure { + with_timers = false, +} + +--- Checks if x is a positive integer (excluding 0) +-- @param x value to be checked +-- @return true if x is a non-zero positive integer; otherwise false. +local function is_positive_integer(x) + return type(x) == 'number' and x % 1 == 0 and x > 0 +end + +--- Checks if the thread pool args are valid. +-- @return nil; raises an error if invalid args are passed in. +local function check_threadpool_args(num_threads) + if not is_positive_integer(num_threads) then + error 'num_threads should be an integer > 0' + end +end + +--- Creates and starts a thread. +-- @param func Function the thread should run after startup +-- @return a new thread object +local function make_thread(func) + return lanes.gen('*', func)() +end + +local ThreadPool = {} +local ThreadPool_mt = { + __index = ThreadPool, +} + +--- Creates a new thread pool with a specific number of threads +-- @param num_threads Amount of the threads the pool should have +-- @return thread pool with a specific number of threads +function ThreadPool.new(num_threads) + check_threadpool_args(num_threads) + local linda = lanes.linda() + local thread_pool = { + threads = {}, + linda = linda, + numjobs = 0, + } + + for _ = 1, num_threads do + table.insert(thread_pool.threads, make_thread(MsgProcessor.new(linda))) + end + return setmetatable(thread_pool, ThreadPool_mt) +end + +--- Schedules a task to a thread in the thread pool +-- @param task A function that should be run on the thread +function ThreadPool:schedule(task) + self.numjobs = self.numjobs + 1 + self.linda:send(nil, MsgProcessor.TASK_TAG, task) +end + +--- Stops all threads in the threadpool. Blocks until all threads are finished +-- @return a table containing all results (in no specific order) +function ThreadPool:join() + map(self.threads, function() + self:schedule(MsgProcessor.STOP_VALUE) + end) + map(self.threads, function(thread) + thread:join() + end) + + local results = {} + for _ = 1, self.numjobs - #self.threads do -- don't count stop job at end + local _, result = self.linda:receive(nil, MsgProcessor.RESULT_TAG) + if result ~= MsgProcessor.VOID_RESULT then + table.insert(results, result) + end + end + return results +end + +return ThreadPool + diff --git a/tools/lua-gdb/README.md b/tools/lua-gdb/README.md new file mode 100644 index 0000000..ff78b67 --- /dev/null +++ b/tools/lua-gdb/README.md @@ -0,0 +1,113 @@ +# Lua-gdb +gdb extension for lua5.3+. + +tested on GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1. + +## Features + +### Pretty printer + +* struct TValue +* struct TString +* struct Table +* struct LClosure +* struct CClosure +* struct lua_State + +### Command +* luacoroutines [L] + +List all coroutines. Without arguments, uses the current value of "L" as the lua_State*. You can provide an alternate lua_State as the first argument. + + +* luastack [L] + +Prints values on the Lua C stack. Without arguments, uses the current value of "L" as the lua_State*. You can provide an alternate lua_State as the first argument. + +* luatraceback [L] + +Dumps Lua execution stack, as debug.traceback() does. Without arguments, uses the current value of "L" as the lua_State*. You can provide an alternate lua_State as the first argument. + +* luagetlocal [L [f]] + +Print all variables of the function at level 'f' of the stack 'coroutine'. With no arguments, Dump all variables of the current funtion in the stack of 'L'. + +## Usage (step by step) + +* compile lua with debug symbols +``` +cd lua-5.3.4 +make linux CFLAGS=-g +``` + +* start gdb +``` +gdb lua-5.3.4/src/lua +``` + +* set a breakpoint +``` +(gdb) break os_time +Breakpoint 1 at 0x42c9fe: file loslib.c, line 324. +``` + +* run `examples/dbg.lua` +``` +(gdb) run examples/dbg.lua +Starting program: /usr/local/bin/lua examples/dbg.lua + +Breakpoint 1, os_time (L=0x64b9c8) at loslib.c:324 +324 static int os_time (lua_State *L) { +``` + +will hit the breakpoint `os_time`. + +* load the extension +``` +(gdb) source lua-gdb.py +Loading Lua Runtime support. +``` + +* list all coroutines +``` +(gdb) luacoroutines +m = {[source] = [C]:-1, [func] = 0x427ff9 } + = {[source] = [C]:-1, [func] = 0x42c9f2 } + = {[source] = [C]:-1, [func] = ?} +``` + +* dump stack +``` +(gdb) luastack 0x64b9c8 +#0 0x64bb30 +#1 0x64bb20 2 +#2 0x64bb10 5 +#3 0x64bb00 10 +#4 0x64baf0 "kkk" +#5 0x64bae0 1 +#6 0x64bad0 "nil" +#7 0x64bac0 "nil" +#8 0x64bab0 = {[file] = "@examples/dbg.lua", [linestart] = 17, [lineend] = 20, [nupvalues] = 1 '\001'} +``` + +* dump traceback +``` +(gdb) luatraceback 0x64b9c8 +stack traceback: + [C]:-1: in 0x42c9f2 + "@examples/dbg.lua":19: in ? +``` + +* list all variables of a closure in the traceback +``` +(gdb) luagetlocal 0x64b9c8 1 +call info: "@examples/dbg.lua":19: in ? + upval _ENV = 3.2627937150349253e-317 + ..... (*vararg) = 1 + ..... (*vararg) = "kkk" + local x = 10 + local i = 5 + local n = 2 +``` + +* enjoy it! diff --git a/tools/lua-gdb/examples/dbg.lua b/tools/lua-gdb/examples/dbg.lua new file mode 100644 index 0000000..23966ce --- /dev/null +++ b/tools/lua-gdb/examples/dbg.lua @@ -0,0 +1,36 @@ +local t = { + "hello", + "world", + vacant = nil, + boolean = true, + int = 5, + float = 3.83, + shrstr = 'short', + lngstr = 'long ' .. string.rep('s', 50), + func = function() end, + co = coroutine.create(function() end), +} + +-- loop +t.t = t + +local function d1(x, i, ...) + local n = select('#', ...) + return os.time() + x + i + n +end + +local function d0(x, t) + local limit = math.random(20) + if x > limit then + local c = d1(0, 1) + 5 + end + + local co = coroutine.create(d1) + local i = 5 + local j = 1 + local k = "kkk" + local ok, v = coroutine.resume(co, x, i, j, k) + return v +end + +print(d0(10, t)) diff --git a/tools/lua-gdb/lua-gdb.py b/tools/lua-gdb/lua-gdb.py new file mode 100644 index 0000000..d8039b0 --- /dev/null +++ b/tools/lua-gdb/lua-gdb.py @@ -0,0 +1,671 @@ +from __future__ import print_function +import re +import sys + +print("Loading Lua Runtime support.", file=sys.stderr) +#http://python3porting.com/differences.html +if sys.version > '3': + xrange = range + +# allow to manually reload while developing +objfile = gdb.current_objfile() or gdb.objfiles()[0] +objfile.pretty_printers = [] + + +# gdb.Value to specific type tt +def cast_to_type_pointer(o, tt): + t = gdb.lookup_type(tt) + return o.cast(t.pointer()) + +# basic types +LUA_TNIL =0 +LUA_TBOOLEAN =1 +LUA_TLIGHTUSERDATA =2 +LUA_TNUMBER =3 +LUA_TSTRING =4 +LUA_TTABLE =5 +LUA_TFUNCTION =6 +LUA_TUSERDATA =7 +LUA_TTHREAD =8 + +def makevariant(t, v): return t | (v << 4) +def ctb(t): return t | (1 << 6) + +LUA_VFALSE = makevariant(LUA_TBOOLEAN, 0) +LUA_VTRUE = makevariant(LUA_TBOOLEAN, 1) + +# test type +def checktype(o, t): return o['tt_']&0x0f == t +def checktag(o, t): return o['tt_'] == t + +# input GCObject* +def cast_u(o): + return cast_to_type_pointer(o, "union GCUnion") + +# -> +def ivalue(o): return o['value_']['i'] + +# -> +def fltvalue(o): return o['value_']['n'] + +# -> +def pvalue(o): return o['value_']['p'] + +# -> +def tsvalue(o): return cast_u(o['value_']['gc'])['ts'] + +# -> +def clLvalue(o): return cast_u(o['value_']['gc'])['cl']['l'] + +# -> +def clCvalue(o): return cast_u(o['value_']['gc'])['cl']['c'] + +# -> +def fvalue(o): return o['value_']['f'] + +# ->
+def hvalue(o): return cast_u(o['value_']['gc'])['h'] + +# -> +def bvalue(o): return checktype(o, LUA_VTRUE) + +# -> +def thvalue(o): return cast_u(o['value_']['gc'])['th'] + +LUA_VNUMINT = makevariant(LUA_TNUMBER, 0) +LUA_VNUMFLT = makevariant(LUA_TNUMBER, 1) + +def ttisnumber(o): return checktype(o, LUA_TNUMBER) +def ttisfloat(o): return checktag(o, LUA_VNUMFLT) +def ttisinteger(o): return checktag(o, LUA_VNUMINT) +def ttisnil(o): return checktype(o, LUA_TNIL) +def ttisboolean(o): return checktype(o, LUA_TBOOLEAN) + +LUA_VLIGHTUSERDATA = makevariant(LUA_TLIGHTUSERDATA, 0) +LUA_VUSERDATA = makevariant(LUA_TUSERDATA, 0) + +def ttislightuserdata(o): return checktag(o, LUA_VLIGHTUSERDATA) +def ttisfulluserdata(o): return checktag(o, ctb(LUA_VUSERDATA)) + +LUA_VSHRSTR = makevariant(LUA_TSTRING, 0) +LUA_VLNGSTR = makevariant(LUA_TSTRING, 1) + +def ttisstring(o): return checktype(o, LUA_TSTRING) +def ttisshrstring(o): return checktype(o, ctb(LUA_VSHRSTR)) +def ttislngstring(o): return checktype(o, ctb(LUA_VLNGSTR)) + +LUA_VTABLE = makevariant(LUA_TTABLE, 0) + +def ttistable(o): return checktag(o, ctb(LUA_VTABLE)) + +LUA_VLCL = makevariant(LUA_TFUNCTION, 0) +LUA_VLCF = makevariant(LUA_TFUNCTION, 1) +LUA_VCCL = makevariant(LUA_TFUNCTION, 2) + +def ttisfunction(o): return checktype(o, LUA_TFUNCTION) +def ttisclosure(o): return o['tt_'] & 0x1f == LUA_VLCL +def ttisCclosure(o): return checktag(o, ctb(LUA_VCCL)) +def ttisLclosure(o): return checktag(o, ctb(LUA_VLCL)) +def ttislcf(o): return checktag(o, LUA_VLCF) + +LUA_VTHREAD = makevariant(LUA_TTHREAD, 0) + +def ttisthread(o): return checktag(o, ctb(LUA_VTHREAD)) + +# gdb.Value to string +def value_to_string(val): + s = str(val.dereference()) + if len(s) > 1 and s[0] == '"' and s[-1] == '"': + return s[1:-1] + return s + +def cast_luaState(o): + return cast_to_type_pointer(o, "struct lua_State") + +# +# Value wrappers +# + +# StackValue to TValue +def s2v(stk): return stk['val'] + +# struct lua_TValue +class TValueValue: + "Wrapper for TValue value." + + def __init__(self, val): + self.val = val + + def upvars(self): + if ttisCclosure(self.val): + f = clCvalue(self.val) + for i in xrange(f['nupvalues']): + yield "(%d)" % (i+1), cast_to_type_pointer(f['upvalue'], "TValue") + i + elif ttisLclosure(self.val): + f = clLvalue(self.val) + proto = f['p'] + for i in xrange(int(proto['sizeupvalues'])): + uv = cast_to_type_pointer(f['upvals'][i], "struct UpVal") + value = uv['v'] + name = (proto['upvalues'] + i)['name'] + if name: + yield value_to_string(name), value + else: + yield "(no name)", value + +# struct CallInfo +class CallInfoValue: + "Wrapper for CallInfo value." + + CIST_C = 1<<1 + CIST_TAIL = 1<<5 + CIST_FIN = 1<<7 + + def __init__(self, L, ci): + self.L = L + self.ci = ci + + self.name = None + self.namewhat = None + + if self.is_lua(): + proto = clLvalue(s2v(self.ci['func']))['p'] + self.proto = proto + + if not proto['source']: + self.source = "?" + else: + self.source = proto['source'].dereference() + + self.linedefined = proto['linedefined'] + self.lastlinedefined = proto['lastlinedefined'] + + if self.linedefined == 0: + self.what = "main" + else: + self.what = "Lua" + + self.currentpc = (self.ci['u']['l']['savedpc'] - proto['code']) - 1 + self.currentline = self.getfuncline(proto, self.currentpc) + + else: + self.source = "[C]" + self.linedefined = -1 + self.lastlinedefined = -1 + self.what = "C" + self.currentline = -1 + + if self.is_fin(): + self.name = "__gc" + self.namewhat = "metamethod" + + def getfuncline(self, proto, pc): + """ + ldebug.c luaG_getfuncline + """ + if not proto['lineinfo']: + return -1 + def getbaseline(proto, pc): + if proto['sizeabslineinfo'] == 0 or pc < proto['abslineinfo'][0]['pc']: + return -1, proto['linedefined'] + if pc >= proto['abslineinfo'][proto['sizeabslineinfo']-1]['pc']: + i = proto['sizeabslineinfo']-1 + else: + j = proto['sizeabslineinfo']-1 + i = 0 + while i < j - 1: + m = (j + i) / 2 + if pc >= proto['abslineinfo'][m]['pc']: + i = m + else: + j = m + return proto['abslineinfo'][i]['pc'], proto['abslineinfo'][i]['line'] + basepc, baseline = getbaseline(proto, pc) + while basepc < pc: + basepc+=1 + baseline += proto['lineinfo'][basepc] + return baseline + + @property + def funcname(self): + if self.what == "main": + return "main chunk" + + if self.namewhat: + return "%s '%s'" % (self.namewhat, self.name) + + func = s2v(self.ci['func']) + if ttislcf(func): + return "%s" % fvalue(func) + + if ttisCclosure(func): + return "%s" % clCvalue(func)['f'] + + return '?' + + def is_lua(self): + return not (self.ci['callstatus'] & CallInfoValue.CIST_C) + + def is_tailcall(self): + return self.ci['callstatus'] & CallInfoValue.CIST_TAIL + + def is_fin(self): + return self.ci['callstatus'] & CallInfoValue.CIST_FIN + + # stack frame information + def frame_info(self): + return '%s:%d: in %s' % (self.source, self.currentline, self.funcname) + + # luastack: + # vararg(1) + # ... + # vararg(nextraargs) <- ci->u.l.nextraargs nextra vararg + # callee <- ci->func + # arg(1) + # ... + # arg(n) + # local(1) + # ... + # local(n) + @property + def stack_base(self): + return self.ci['func'] + 1 + + @property + def stack_top(self): + if self.ci == self.L['ci']: + return self.L['top'] + else: + nextcv = CallInfoValue(self.L, self.ci['next']) + return nextcv.stack_base - 1 + + def getlocalname(self, n): + if not self.is_lua(): + return None + + proto = self.proto + currentpc = self.currentpc + + i = 0 + while i< proto['sizelocvars']: + locvar = proto['locvars'] + i + if locvar['startpc'] <= currentpc and currentpc < locvar['endpc']: + n = n - 1 + if n == 0: + return value_to_string(locvar['varname']) + i = i + 1 + return None + + def upvars(self): + tv = TValueValue(s2v(self.ci['func'])) + return tv.upvars() + + def varargs(self): + if not self.is_lua(): + return + + if self.proto['is_vararg'] != 1: + return + + nextra = self.ci['u']['l']['nextraargs'] + for i in xrange(nextra): + yield "(*vararg)", s2v(self.ci['func'] - (i+1)).address + + def locvars(self): + base = self.stack_base + limit = self.stack_top + i = 1 + while True: + name = self.getlocalname(i) + if not name: + if (limit - base) >= i: + if self.is_lua(): + name = "(temporary)" + else: + name = "(C temporary)" + else: + return + yield name, s2v(base + i - 1).address + i = i + 1 + +# +# Pretty Printers +# + +def tvaluestring(value): + if ttisnil(value): # nil + return "nil" + elif ttisboolean(value): # boolean + if bvalue(value) > 0: + return "True" + else: + return "False" + elif ttisnumber(value): # number + if ttisfloat(value): + return fltvalue(value) + elif ttisinteger(value): + return ivalue(value) + elif ttisstring(value): # string + return tsvalue(value) + elif ttistable(value): # table + return hvalue(value) + elif ttisfunction(value): + if ttisLclosure(value): # lua closure + return clLvalue(value) + elif ttislcf(value): # light C function + return fvalue(value) + elif ttisCclosure(value): # 2 C closure + return clCvalue(value) + elif ttisfulluserdata(value): + return "Userdata" + elif ttislightuserdata(value): # lightuserdata + return "" % int(pvalue(value)) + elif ttisthread(value): + return thvalue(value) + assert False, value['tt_'] + +class TValuePrinter: + "Pretty print lua value." + + pattern = re.compile(r'^(struct TValue)|(TValue)$') + + def __init__(self, val): + self.val = val + + def to_string(self): + return tvaluestring(self.val) + + def display_hint(self): + return "string" + +class TStringPrinter: + "Pretty print lua string." + + pattern = re.compile(r'^(struct TString)|(TString)$') + + def __init__(self, val): + self.val = val + + def display_hint(self): + return "string" + + def to_string(self): + s = self.val["contents"] + return s.cast(gdb.lookup_type('char').pointer()) + +class TablePrinter: + "Pretty print lua table." + + pattern = re.compile(r'^(struct Table)|(Table)$') + marked = None + + def __init__(self, val): + self.val = val + + def display_hint(self): + return "map" + + def to_string(self): + return "
" % int(self.val.address) + + def children(self): + setMarked = False + if TablePrinter.marked == None: + TablePrinter.marked = {} + setMarked = True + + address = int(self.val.address) + if address in TablePrinter.marked: + return TablePrinter.marked[address] + TablePrinter.marked[address] = self.to_string() + + # array part + sizearray = self.realasize() + i = 0 + while i < sizearray: + val = self.val['array'][i] + if ttisnil(val): + continue + yield str(2*i), i + yield str(2*i + 1), val + i = i + 1 + + # hash part + j = 0 + last = 1 << self.val['lsizenode'] + while j < last: + node = self.val['node'][j] + j = j + 1 + value = node['i_val'] + if ttisnil(value): + continue + fakeTValue = { + "tt_": node['u']['key_tt'], + "value_": node['u']['key_val'] + } + yield str(2*i + 2*j), tvaluestring(fakeTValue) + yield str(2*i + 2*j + 1), value + + if setMarked: + TablePrinter.marked = None + + def realasize(self): + def isrealasize(self): return (self.val['flags'] & (1<<7)) == 0 + def ispow2(x): return (((x) & ((x) - 1)) == 0) + if (isrealasize(self) or ispow2(self.val['alimit'])): + return self.val['alimit'] + else: + size = self.val['alimit'] + size |= (size >> 1) + size |= (size >> 2) + size |= (size >> 4) + size |= (size >> 8) + size |= (size >> 16) + size += 1 + return size + + +class LClosurePrinter: + "Pretty print lua closure." + + pattern = re.compile(r'^(struct LClosure)|(LClosure)$') + + def __init__(self, val): + self.val = val + + def display_hint(self): + return "map" + + def to_string(self): + return "" % int(self.val.address) + + def children(self): + p = self.val['p'] + yield "1", "file" + yield "2", p['source'].dereference() + yield "3", "linestart" + yield "4", p['linedefined'] + yield "5", "lineend" + yield "6", p['lastlinedefined'] + yield "7", "nupvalues" + yield "8", self.val['nupvalues'] + +class CClosurePrinter: + "Pretty print lua closure." + + pattern = re.compile(r'^(struct CClosure)|(CClosure)$') + + def __init__(self, val): + self.val = val + + def display_hint(self): + return "map" + + def to_string(self): + return "" % int(self.val.address) + + def children(self): + yield "1", "nupvalues" + yield "2", self.val['nupvalues'] + +class LuaStatePrinter: + "Pretty print lua_State." + + pattern = re.compile(r'^struct lua_State$') + + def __init__(self, val): + self.val = val + + def display_hint(self): + return "map" + + def to_string(self): + return "" % int(self.val.address) + + def children(self): + cv = CallInfoValue(self.val, self.val['ci']) + yield "1", "source" + yield "2", "%s:%d" % (cv.source, cv.currentline) + yield "3", "func" + yield "4", cv.funcname + +# +# Register all the *Printer classes above. +# + +def makematcher(klass): + def matcher(val): + try: + if klass.pattern.match(str(val.type)): + return klass(val) + except Exception: + pass + return matcher + +objfile.pretty_printers.extend([makematcher(var) for var in vars().values() if hasattr(var, 'pattern')]) + +class LuaStackCmd(gdb.Command): + """luastack [L] +Prints values on the Lua C stack. Without arguments, uses the current value of "L" +as the lua_State*. You can provide an alternate lua_State as the first argument.""" + + def __init__(self): + gdb.Command.__init__(self, "luastack", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) + + def invoke(self, args, _from_tty): + argv = gdb.string_to_argv(args) + if len(argv) > 0: + L = cast_luaState(gdb.parse_and_eval(argv[0])) + else: + L = gdb.parse_and_eval("L") + + stack = L['top'] - 1 + i = 0 + while stack > L['stack']: + print("#%d\t0x%x\t%s" % (i, int(stack), stack.dereference())) + stack = stack - 1 + i = i + 1 + +class LuaTracebackCmd(gdb.Command): + """luabacktrace [L] +Dumps Lua execution stack, as debug.traceback() does. Without +arguments, uses the current value of "L" as the +lua_State*. You can provide an alternate lua_State as the +first argument. + """ + def __init__(self): + gdb.Command.__init__(self, "luatraceback", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) + + def invoke(self, args, _from_tty): + argv = gdb.string_to_argv(args) + if len(argv) > 0: + L = cast_luaState(gdb.parse_and_eval(argv[0])) + else: + L = gdb.parse_and_eval("L") + + ci = L['ci'] + print("stack traceback:") + while ci != L['base_ci'].address: + cv = CallInfoValue(L, ci) + print('\t%s' % (cv.frame_info())) + if cv.is_tailcall(): + print('\t(...tail calls...)') + ci = ci['previous'] + + +class LuaCoroutinesCmd(gdb.Command): + """luacoroutines [L] +List all coroutines. Without arguments, uses the current value of "L" as the +lua_State*. You can provide an alternate lua_State as the +first argument. + """ + def __init__(self): + gdb.Command.__init__(self, "luacoroutines", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) + + def invoke(self, args, _from_tty): + argv = gdb.string_to_argv(args) + if len(argv) > 0: + L = cast_luaState(gdb.parse_and_eval(argv[0])) + else: + L = gdb.parse_and_eval("L") + + # global_State + lG = L['l_G'] + + # mainthread + print("m", lG['mainthread'].dereference()) + + obj = lG['allgc'] + while obj: + if obj['tt'] == 8: + print(" ", cast_u(obj)['th']) + obj = obj['next'] + +class LuaGetLocalCmd(gdb.Command): + """luagetlocal [L [f]] +Print all local variables of the function at level 'f' of the stack 'thread'. +With no arguments, Dump all local variable of the current funtion in the stack of 'L'; + """ + def __init__(self): + gdb.Command.__init__(self, "luagetlocal", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) + + def invoke(self, args, _from_tty): + argv = gdb.string_to_argv(args) + if len(argv) > 0: + L = cast_luaState(gdb.parse_and_eval(argv[0])) + else: + L = gdb.parse_and_eval("L") + + if len(argv) > 1: + arg2 = gdb.parse_and_eval(argv[1]) + else: + arg2 = gdb.parse_and_eval("0") + + level = arg2 + ci = L['ci'] + while level > 0: + ci = ci['previous'] + if ci == L['base_ci'].address: + break + level = level - 1 + + if level != 0: + print("No function at level %d" % arg2) + return + + cv = CallInfoValue(L, ci) + print("call info: %s" % cv.frame_info()) + + for name, var in cv.upvars(): + print("\tupval %s = %s" % (name, var.dereference())) + + for name, var in cv.varargs(): + print("\t..... %s = %s" % (name, var.dereference())) + + for name, var in cv.locvars(): + print("\tlocal %s = %s" % (name, var.dereference())) + +LuaStackCmd() +LuaTracebackCmd() +LuaCoroutinesCmd() +LuaGetLocalCmd() \ No newline at end of file diff --git a/website/backend/.gitignore b/website/backend/.gitignore new file mode 100755 index 0000000..b0ffa7e --- /dev/null +++ b/website/backend/.gitignore @@ -0,0 +1,50 @@ +# lor +client_body_temp +fastcgi_temp +logs +proxy_temp +tmp +uwsgi_temp + +# Compiled Lua sources +luac.out + +# luarocks build files +*.src.rock +*.zip +*.tar.gz + +# Object files +*.o +*.os +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo +*.def +*.exp + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +www/admin/ \ No newline at end of file diff --git a/website/backend/README.md b/website/backend/README.md new file mode 100755 index 0000000..1631e12 --- /dev/null +++ b/website/backend/README.md @@ -0,0 +1,15 @@ + +## Installation + + +1 安装 [openresty](https://openresty.org) + +2 了解 [lor](https://github.com/sumory/lor) + +3 修改 [相关配置](https://github.com/cloudfreexiao/RillAdmin/issues/1) + +4 把 frontend 工程打包好的 资源 copy 到 [www 目录](https://github.com/cloudfreexiao/RillAdmin/tree/master/backend-or/www) + +5 执行 start shell + +6 访问 [http://localhost:9527](http://localhost:9527). diff --git a/website/backend/app/config/config.lua b/website/backend/app/config/config.lua new file mode 100755 index 0000000..fbf59a5 --- /dev/null +++ b/website/backend/app/config/config.lua @@ -0,0 +1,59 @@ +return { + -- 白名单配置:不需要登录即可访问;除非要二次开发,否则不应更改 + whitelist = { + "^/api/login$", -- login page + "^/api/sign_up$", -- sign up page + -- "^/api/user$", + "^/error/$" -- error page + }, + + -- 静态模板配置,保持默认不修改即可 + view_config = { + engine = "tmpl", + ext = "html", + views = "./app/views" + }, + + + -- 分页时每页条数配置 + page_config = { + index_topic_page_size = 10, -- 首页每页文章数 + }, + + + + -- ########################## 以下配置需要使用者自定义为本地需要的配置 ########################## -- + + -- 生成session的secret,请一定要修改此值为一复杂的字符串,用于加密session + session_secret = "3584827dfed45b40328acb6242bhngod", + + -- 用于存储密码的盐,请一定要修改此值, 一旦使用不能修改,用户也可自行实现其他密码方案 + pwd_secret = "salt_secret_for_password", + jwt_secret = "hjuhdk_jjdkdh763nnjhf", --jwt 私钥,用于加密 + + -- mysql配置 + --ERROR 1045 (28000): Access denied for user 'root'@'192.168.1.25' (using password: YES) + --原因是 非本机不可以root 登录 + mysql = { + timeout = 5000, + connect_config = { + host = "192.168.1.14", + port = 3306, + database = "fishadmin", + user = "fish", + password = "111111", + max_packet_size = 1024 * 1024 + }, + pool_config = { + max_idle_timeout = 20000, -- 20s + pool_size = 50 -- connection pool size + } + }, + + -- 上传文件配置,如上传的头像、文章中的图片等 + upload_config = { + dir = "/opt/fishadmin/static", -- 文件目录,修改此值时须同时修改nginx配置文件中的$static_files_path值 + }, + + cors_whitelist = "http://192.168.1.35:9527", --请修改自己对应的 frontend url +} \ No newline at end of file diff --git a/website/backend/app/config/error_code.lua b/website/backend/app/config/error_code.lua new file mode 100755 index 0000000..9d7f213 --- /dev/null +++ b/website/backend/app/config/error_code.lua @@ -0,0 +1,30 @@ +local errors = {} + +function system_error_msg(ec) + if not ec then + return "nil" + end + return errors[ec].desc +end + +local function add(err) + assert(errors[err.code] == nil, string.format("have the same error code[%x], msg[%s]", err.code, err.message)) + errors[err.code] = {code = err.code, desc = err.desc} + return err.code +end + +SYSTEM_ERROR = { + success = add{code = 0x0000, desc = "请求成功"}, + failed = add{code = 0x0001, desc = "操作失败"}, +} + +AUTH_ERROR = { + account_error = add{code = 0x0101, desc = "用户名或密码错误,请检查!"}, + account_nil = add{code = 0x0102, desc = "用户名和密码不得为空!"}, + account_login = add{code = 0x0103, desc = "该操作需要先登录!"}, + +} + + + +return errors \ No newline at end of file diff --git a/website/backend/app/controller/user/login.lua b/website/backend/app/controller/user/login.lua new file mode 100755 index 0000000..58d8200 --- /dev/null +++ b/website/backend/app/controller/user/login.lua @@ -0,0 +1,66 @@ +local utils = require("app.libs.utils") +-- local jwt = require("app.libs.jwt.jwt") + +local pwd_secret = require("app.config.config").pwd_secret +local jwt_secret = require("app.config.config").jwt_secret + +local user_model = require("app.model.user") + +return function (req, username, password) + if not username or not password or username == "" or password == "" then + return { + code = AUTH_ERROR.account_nil, + message = system_error_msg(AUTH_ERROR.account_nil), + } + end + + local isExist = false + local userid = 0 + + password = utils.encode(password .. "#" .. pwd_secret) + local result, err = user_model:query(username, password) + + local user = {} + if result and not err then + if result and #result == 1 then + isExist = true + user = result[1] + userid = user.id + end + else + isExist = false + end + + -- 生成 token 的有效期 + local now = ngx.now() + local exp = now + 1200 + + if isExist == true then + -- local jwt_token = jwt:sign(jwt_secret, { + -- header = { typ = "JWT", alg = "HS256" }, + -- payload = { foo = "bar", id = 1, name = "mind029", exp = exp } + -- }) + + local token = ngx.md5(username .. password .. os.time() .. "fishadminapi") + req.session.set("user", { + username = username, + userid = userid, + create_time = user.create_time or "", + token = token + }) + + return { + code = SYSTEM_ERROR.success, + message = system_error_msg(SYSTEM_ERROR.success), + data = { + token = token + } + } + else + return { + code = AUTH_ERROR.account_error, + message = system_error_msg(AUTH_ERROR.account_error), + } + end + +end \ No newline at end of file diff --git a/website/backend/app/controller/user/signup.lua b/website/backend/app/controller/user/signup.lua new file mode 100755 index 0000000..6849b6e --- /dev/null +++ b/website/backend/app/controller/user/signup.lua @@ -0,0 +1,78 @@ +local pairs = pairs +local ipairs = ipairs +local smatch = string.match +local slen = string.len +local ssub = string.sub +local slower = string.lower + +local utils = require("app.libs.utils") +local pwd_secret = require("app.config.config").pwd_secret + +local user_model = require("app.model.user") + +local role_lv = 1 --角色等级 用来 鉴权操作 + +return function(username, password) + local pattern = "^[a-zA-Z][0-9a-zA-Z_]+$" + local match, err = smatch(username, pattern) + + if not username or not password or username == "" or password == "" then + return { + code = system_error_msg(AUTH_ERROR.account_nil), + message = system_error_msg(AUTH_ERROR.account_nil), + } + end + + local username_len = slen(username) + local password_len = slen(password) + + if username_len<4 or username_len>50 then + return { + success = false, + msg = "用户名长度应为4~50位." + } + end + if password_len<6 or password_len>50 then + return { + success = false, + msg = "密码长度应为6~50位." + } + end + + if not match then + return { + success = false, + msg = "用户名只能输入字母、下划线、数字,必须以字母开头." + } + end + + local result, err = user_model:query_by_username(username) + local isExist = false + if result and not err then + isExist = true + end + + if isExist == true then + return { + success = false, + msg = "用户名已被占用,请修改." + } + else + password = utils.encode(password .. "#" .. pwd_secret) + local avatar = ssub(username, 1, 1) .. ".png" --取首字母作为默认头像名 + avatar = slower(avatar) + local result, err = user_model:new(username, password, avatar, role_lv) + if result and not err then + return { + success = true, + msg = "注册成功." + } + else + return { + success = false, + msg = "注册失败." + } + end + end + +end \ No newline at end of file diff --git a/website/backend/app/controller/user/user_info.lua b/website/backend/app/controller/user/user_info.lua new file mode 100755 index 0000000..59959b0 --- /dev/null +++ b/website/backend/app/controller/user/user_info.lua @@ -0,0 +1,29 @@ + +local function is_login(req) + local user + if req.session then + user = req.session.get("user") + if user and user.username and user.userid then + return true, user + end + end + return false, nil +end + +return function (req, res) + local user = is_login(req) + assert(user) + local msg = { + code = SYSTEM_ERROR.success, + message = system_error_msg(SYSTEM_ERROR.success), + data = { + name = "goodname", + avatar = "333333", + roles = {[1] = "/api/getRoles", + [2] = "/api/role",}, + permissions = {[1]="/api/permissions/"}, + } + + } + return msg +end \ No newline at end of file diff --git a/website/backend/app/cors_header.lua b/website/backend/app/cors_header.lua new file mode 100755 index 0000000..992fe5d --- /dev/null +++ b/website/backend/app/cors_header.lua @@ -0,0 +1,11 @@ +local cors_whitelist = require("app.config.config").cors_whitelist + +--跨域访问 头 设置 +return function (res) + res:set_header("X-Powered-By", "Lor framework") + res:set_header("Access-Control-Allow-Origin", cors_whitelist) + + res:set_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + res:set_header("Access-Control-Allow-Credentials", "true") + res:set_header("Access-Control-Allow-Headers", "Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since,X-Msys-Subaccount,X-Sparky") +end \ No newline at end of file diff --git a/website/backend/app/librarys/README.md b/website/backend/app/librarys/README.md new file mode 100755 index 0000000..d5c634a --- /dev/null +++ b/website/backend/app/librarys/README.md @@ -0,0 +1,4 @@ + +### lua c so 存放目录() + + diff --git a/website/backend/app/libs/cors.lua b/website/backend/app/libs/cors.lua new file mode 100755 index 0000000..3ad2766 --- /dev/null +++ b/website/backend/app/libs/cors.lua @@ -0,0 +1,95 @@ +-- @Author: detailyang +-- @Date: 2016-10-10 15:45:33 +-- @Last Modified by: detailyang +-- @Last Modified time: 2017-02-19 13:29:41 + +-- https://www.w3.org/TR/cors/ + +local re_match = ngx.re.match + +local _M = { _VERSION = '0.1.0'} + +local Origin = 'Origin' +local AccessControlAllowOrigin = 'Access-Control-Allow-Origin' +local AccessControlExposeHeaders = 'Access-Control-Expose-Headers' +local AccessControlMaxAge = 'Access-Control-Max-Age' +local AccessControlAllowCredentials = 'Access-Control-Allow-Credentials' +local AccessControlAllowMethods = 'Access-Control-Allow-Methods' +local AccessControlAllowHeaders = 'Access-Control-Allow-Headers' + +local mt = { __index = _M } + +local allow_hosts = {} +local allow_headers = {} +local allow_methods = {} +local expose_headers = {} +local max_age = 3600 +local allow_credentials = true +local join = table.concat + + +function _M.allow_host(host) + allow_hosts[#allow_hosts + 1] = host +end + +function _M.allow_method(method) + allow_methods[#allow_methods + 1] = method +end + +function _M.allow_header(header) + allow_headers[#allow_headers + 1] = header +end + +function _M.expose_header(header) + expose_headers[#expose_headers + 1] = header +end + +function _M.max_age(age) + max_age = age +end + +function _M.allow_credentials(credentials) + allow_credentials = credentials +end + +function _M.run() + local origin = ngx.req.get_headers()[Origin] + if not origin then + return + end + + local matched = false + for k, v in pairs(allow_hosts) do + local from, to, err = ngx.re.find(origin, v, "jo") + if from then + matched = true + end + end + + if matched == false then + return + end + + ngx.header[AccessControlAllowOrigin] = origin + ngx.header[AccessControlMaxAge] = max_age + + if #expose_headers >= 0 then + ngx.header[AccessControlExposeHeaders] = join(expose_headers, ',') + end + + if #allow_headers >= 0 then + ngx.header[AccessControlAllowHeaders] = join(allow_headers, ',') + end + + if #allow_methods >= 0 then + ngx.header[AccessControlAllowMethods] = join(allow_methods, ',') + end + + if allow_credentials == true then + ngx.header[AccessControlAllowCredentials] = "true" + else + ngx.header[AccessControlAllowCredentials] = "false" + end +end + +return _M diff --git a/website/backend/app/libs/date.lua b/website/backend/app/libs/date.lua new file mode 100755 index 0000000..6010cda --- /dev/null +++ b/website/backend/app/libs/date.lua @@ -0,0 +1,747 @@ +-- https://github.com/Tieske/date +--------------------------------------------------------------------------------------- +-- Module for date and time calculations +-- +-- Version 2.1.1 +-- Copyright (C) 2006, by Jas Latrix (jastejada@yahoo.com) +-- Copyright (C) 2013-2014, by Thijs Schreijer +-- Licensed under MIT, http://opensource.org/licenses/MIT + +--[[ CONSTANTS ]]-- + local HOURPERDAY = 24 + local MINPERHOUR = 60 + local MINPERDAY = 1440 -- 24*60 + local SECPERMIN = 60 + local SECPERHOUR = 3600 -- 60*60 + local SECPERDAY = 86400 -- 24*60*60 + local TICKSPERSEC = 1000000 + local TICKSPERDAY = 86400000000 + local TICKSPERHOUR = 3600000000 + local TICKSPERMIN = 60000000 + local DAYNUM_MAX = 365242500 -- Sat Jan 01 1000000 00:00:00 + local DAYNUM_MIN = -365242500 -- Mon Jan 01 1000000 BCE 00:00:00 + local DAYNUM_DEF = 0 -- Mon Jan 01 0001 00:00:00 + local _; +--[[ LOCAL ARE FASTER ]]-- + local type = type + local pairs = pairs + local error = error + local assert = assert + local tonumber = tonumber + local tostring = tostring + local string = string + local math = math + local os = os + local unpack = unpack or table.unpack + local pack = table.pack or function(...) return { n = select('#', ...), ... } end + local setmetatable = setmetatable + local getmetatable = getmetatable +--[[ EXTRA FUNCTIONS ]]-- + local fmt = string.format + local lwr = string.lower + local upr = string.upper + local rep = string.rep + local len = string.len + local sub = string.sub + local gsub = string.gsub + local gmatch = string.gmatch or string.gfind + local find = string.find + local ostime = os.time + local osdate = os.date + local floor = math.floor + local ceil = math.ceil + local abs = math.abs + -- removes the decimal part of a number + local function fix(n) n = tonumber(n) return n and ((n > 0 and floor or ceil)(n)) end + -- returns the modulo n % d; + local function mod(n,d) return n - d*floor(n/d) end + -- rounds a number; + local function round(n, d) d=d^10 return floor((n*d)+.5)/d end + -- rounds a number to whole; + local function whole(n)return floor(n+.5)end + -- is `str` in string list `tbl`, `ml` is the minimun len + local function inlist(str, tbl, ml, tn) + local sl = len(str) + if sl < (ml or 0) then return nil end + str = lwr(str) + for k, v in pairs(tbl) do + if str == lwr(sub(v, 1, sl)) then + if tn then tn[0] = k end + return k + end + end + end + local function fnil() end + local function fret(x)return x;end +--[[ DATE FUNCTIONS ]]-- + local DATE_EPOCH -- to be set later + local sl_weekdays = { + [0]="Sunday",[1]="Monday",[2]="Tuesday",[3]="Wednesday",[4]="Thursday",[5]="Friday",[6]="Saturday", + [7]="Sun",[8]="Mon",[9]="Tue",[10]="Wed",[11]="Thu",[12]="Fri",[13]="Sat", + } + local sl_meridian = {[-1]="AM", [1]="PM"} + local sl_months = { + [00]="January", [01]="February", [02]="March", + [03]="April", [04]="May", [05]="June", + [06]="July", [07]="August", [08]="September", + [09]="October", [10]="November", [11]="December", + [12]="Jan", [13]="Feb", [14]="Mar", + [15]="Apr", [16]="May", [17]="Jun", + [18]="Jul", [19]="Aug", [20]="Sep", + [21]="Oct", [22]="Nov", [23]="Dec", + } + -- added the '.2' to avoid collision, use `fix` to remove + local sl_timezone = { + [000]="utc", [0.2]="gmt", + [300]="est", [240]="edt", + [360]="cst", [300.2]="cdt", + [420]="mst", [360.2]="mdt", + [480]="pst", [420.2]="pdt", + } + -- set the day fraction resolution + local function setticks(t) + TICKSPERSEC = t; + TICKSPERDAY = SECPERDAY*TICKSPERSEC + TICKSPERHOUR= SECPERHOUR*TICKSPERSEC + TICKSPERMIN = SECPERMIN*TICKSPERSEC + end + -- is year y leap year? + local function isleapyear(y) -- y must be int! + return (mod(y, 4) == 0 and (mod(y, 100) ~= 0 or mod(y, 400) == 0)) + end + -- day since year 0 + local function dayfromyear(y) -- y must be int! + return 365*y + floor(y/4) - floor(y/100) + floor(y/400) + end + -- day number from date, month is zero base + local function makedaynum(y, m, d) + local mm = mod(mod(m,12) + 10, 12) + return dayfromyear(y + floor(m/12) - floor(mm/10)) + floor((mm*306 + 5)/10) + d - 307 + --local yy = y + floor(m/12) - floor(mm/10) + --return dayfromyear(yy) + floor((mm*306 + 5)/10) + (d - 1) + end + -- date from day number, month is zero base + local function breakdaynum(g) + local g = g + 306 + local y = floor((10000*g + 14780)/3652425) + local d = g - dayfromyear(y) + if d < 0 then y = y - 1; d = g - dayfromyear(y) end + local mi = floor((100*d + 52)/3060) + return (floor((mi + 2)/12) + y), mod(mi + 2,12), (d - floor((mi*306 + 5)/10) + 1) + end + --[[ for floats or int32 Lua Number data type + local function breakdaynum2(g) + local g, n = g + 306; + local n400 = floor(g/DI400Y);n = mod(g,DI400Y); + local n100 = floor(n/DI100Y);n = mod(n,DI100Y); + local n004 = floor(n/DI4Y); n = mod(n,DI4Y); + local n001 = floor(n/365); n = mod(n,365); + local y = (n400*400) + (n100*100) + (n004*4) + n001 - ((n001 == 4 or n100 == 4) and 1 or 0) + local d = g - dayfromyear(y) + local mi = floor((100*d + 52)/3060) + return (floor((mi + 2)/12) + y), mod(mi + 2,12), (d - floor((mi*306 + 5)/10) + 1) + end + ]] + -- day fraction from time + local function makedayfrc(h,r,s,t) + return ((h*60 + r)*60 + s)*TICKSPERSEC + t + end + -- time from day fraction + local function breakdayfrc(df) + return + mod(floor(df/TICKSPERHOUR),HOURPERDAY), + mod(floor(df/TICKSPERMIN ),MINPERHOUR), + mod(floor(df/TICKSPERSEC ),SECPERMIN), + mod(df,TICKSPERSEC) + end + -- weekday sunday = 0, monday = 1 ... + local function weekday(dn) return mod(dn + 1, 7) end + -- yearday 0 based ... + local function yearday(dn) + return dn - dayfromyear((breakdaynum(dn))-1) + end + -- parse v as a month + local function getmontharg(v) + local m = tonumber(v); + return (m and fix(m - 1)) or inlist(tostring(v) or "", sl_months, 2) + end + -- get daynum of isoweek one of year y + local function isow1(y) + local f = makedaynum(y, 0, 4) -- get the date for the 4-Jan of year `y` + local d = weekday(f) + d = d == 0 and 7 or d -- get the ISO day number, 1 == Monday, 7 == Sunday + return f + (1 - d) + end + local function isowy(dn) + local w1; + local y = (breakdaynum(dn)) + if dn >= makedaynum(y, 11, 29) then + w1 = isow1(y + 1); + if dn < w1 then + w1 = isow1(y); + else + y = y + 1; + end + else + w1 = isow1(y); + if dn < w1 then + w1 = isow1(y-1) + y = y - 1 + end + end + return floor((dn-w1)/7)+1, y + end + local function isoy(dn) + local y = (breakdaynum(dn)) + return y + (((dn >= makedaynum(y, 11, 29)) and (dn >= isow1(y + 1))) and 1 or (dn < isow1(y) and -1 or 0)) + end + local function makedaynum_isoywd(y,w,d) + return isow1(y) + 7*w + d - 8 -- simplified: isow1(y) + ((w-1)*7) + (d-1) + end +--[[ THE DATE MODULE ]]-- + local fmtstr = "%x %X"; +--#if not DATE_OBJECT_AFX then + local date = {} + setmetatable(date, date) +-- Version: VMMMRRRR; V-Major, M-Minor, R-Revision; e.g. 5.45.321 == 50450321 + date.version = 20010001 -- 2.1.1 +--#end -- not DATE_OBJECT_AFX +--[[ THE DATE OBJECT ]]-- + local dobj = {} + dobj.__index = dobj + dobj.__metatable = dobj + -- shout invalid arg + local function date_error_arg() return error("invalid argument(s)",0) end + -- create new date object + local function date_new(dn, df) + return setmetatable({daynum=dn, dayfrc=df}, dobj) + end + -- is `v` a date object? + local function date_isdobj(v) + return (type(v) == 'table' and getmetatable(v) == dobj) and v + end + +--#if not NO_LOCAL_TIME_SUPPORT then + -- magic year table + local date_epoch, yt; + local function getequivyear(y) + assert(not yt) + yt = {} + local de, dw, dy = date_epoch:copy() + for i = 0, 3000 do + de:setyear(de:getyear() + 1, 1, 1) + dy = de:getyear() + dw = de:getweekday() * (isleapyear(dy) and -1 or 1) + if not yt[dw] then yt[dw] = dy end --print(de) + if yt[1] and yt[2] and yt[3] and yt[4] and yt[5] and yt[6] and yt[7] and yt[-1] and yt[-2] and yt[-3] and yt[-4] and yt[-5] and yt[-6] and yt[-7] then + getequivyear = function(y) return yt[ (weekday(makedaynum(y, 0, 1)) + 1) * (isleapyear(y) and -1 or 1) ] end + return getequivyear(y) + end + end + end + -- TimeValue from daynum and dayfrc + local function dvtotv(dn, df) + return fix(dn - DATE_EPOCH) * SECPERDAY + (df/1000) + end + -- TimeValue from date and time + local function totv(y,m,d,h,r,s) + return (makedaynum(y, m, d) - DATE_EPOCH) * SECPERDAY + ((h*60 + r)*60 + s) + end + -- TimeValue from TimeTable + local function tmtotv(tm) + return tm and totv(tm.year, tm.month - 1, tm.day, tm.hour, tm.min, tm.sec) + end + -- Returns the bias in seconds of utc time daynum and dayfrc + local function getbiasutc2(self) + local y,m,d = breakdaynum(self.daynum) + local h,r,s = breakdayfrc(self.dayfrc) + local tvu = totv(y,m,d,h,r,s) -- get the utc TimeValue of date and time + local tml = osdate("*t", tvu) -- get the local TimeTable of tvu + if (not tml) or (tml.year > (y+1) or tml.year < (y-1)) then -- failed try the magic + y = getequivyear(y) + tvu = totv(y,m,d,h,r,s) + tml = osdate("*t", tvu) + end + local tvl = tmtotv(tml) + if tvu and tvl then + return tvu - tvl, tvu, tvl + else + return error("failed to get bias from utc time") + end + end + -- Returns the bias in seconds of local time daynum and dayfrc + local function getbiasloc2(daynum, dayfrc) + local tvu + -- extract date and time + local y,m,d = breakdaynum(daynum) + local h,r,s = breakdayfrc(dayfrc) + -- get equivalent TimeTable + local tml = {year=y, month=m+1, day=d, hour=h, min=r, sec=s} + -- get equivalent TimeValue + local tvl = tmtotv(tml) + + local function chkutc() + tml.isdst = nil; local tvug = ostime(tml) if tvug and (tvl == tmtotv(osdate("*t", tvug))) then tvu = tvug return end + tml.isdst = true; local tvud = ostime(tml) if tvud and (tvl == tmtotv(osdate("*t", tvud))) then tvu = tvud return end + tvu = tvud or tvug + end + chkutc() + if not tvu then + tml.year = getequivyear(y) + tvl = tmtotv(tml) + chkutc() + end + return ((tvu and tvl) and (tvu - tvl)) or error("failed to get bias from local time"), tvu, tvl + end +--#end -- not NO_LOCAL_TIME_SUPPORT + +--#if not DATE_OBJECT_AFX then + -- the date parser + local strwalker = {} -- ^Lua regular expression is not as powerful as Perl$ + strwalker.__index = strwalker + local function newstrwalker(s)return setmetatable({s=s, i=1, e=1, c=len(s)}, strwalker) end + function strwalker:aimchr() return "\n" .. self.s .. "\n" .. rep(".",self.e-1) .. "^" end + function strwalker:finish() return self.i > self.c end + function strwalker:back() self.i = self.e return self end + function strwalker:restart() self.i, self.e = 1, 1 return self end + function strwalker:match(s) return (find(self.s, s, self.i)) end + function strwalker:__call(s, f)-- print("strwalker:__call "..s..self:aimchr()) + local is, ie; is, ie, self[1], self[2], self[3], self[4], self[5] = find(self.s, s, self.i) + if is then self.e, self.i = self.i, 1+ie; if f then f(unpack(self)) end return self end + end + local function date_parse(str) + local y,m,d, h,r,s, z, w,u, j, e, k, x,v,c, chkfin, dn,df; + local sw = newstrwalker(gsub(gsub(str, "(%b())", ""),"^(%s*)","")) -- remove comment, trim leading space + --local function error_out() print(y,m,d,h,r,s) end + local function error_dup(q) --[[error_out()]] error("duplicate value: " .. (q or "") .. sw:aimchr()) end + local function error_syn(q) --[[error_out()]] error("syntax error: " .. (q or "") .. sw:aimchr()) end + local function error_inv(q) --[[error_out()]] error("invalid date: " .. (q or "") .. sw:aimchr()) end + local function sety(q) y = y and error_dup() or tonumber(q); end + local function setm(q) m = (m or w or j) and error_dup(m or w or j) or tonumber(q) end + local function setd(q) d = d and error_dup() or tonumber(q) end + local function seth(q) h = h and error_dup() or tonumber(q) end + local function setr(q) r = r and error_dup() or tonumber(q) end + local function sets(q) s = s and error_dup() or tonumber(q) end + local function adds(q) s = s + tonumber(q) end + local function setj(q) j = (m or w or j) and error_dup() or tonumber(q); end + local function setz(q) z = (z ~= 0 and z) and error_dup() or q end + local function setzn(zs,zn) zn = tonumber(zn); setz( ((zn<24) and (zn*60) or (mod(zn,100) + floor(zn/100) * 60))*( zs=='+' and -1 or 1) ) end + local function setzc(zs,zh,zm) setz( ((tonumber(zh)*60) + tonumber(zm))*( zs=='+' and -1 or 1) ) end + + if not (sw("^(%d%d%d%d)",sety) and (sw("^(%-?)(%d%d)%1(%d%d)",function(_,a,b) setm(tonumber(a)); setd(tonumber(b)) end) or sw("^(%-?)[Ww](%d%d)%1(%d?)",function(_,a,b) w, u = tonumber(a), tonumber(b or 1) end) or sw("^%-?(%d%d%d)",setj) or sw("^%-?(%d%d)",function(a) setm(a);setd(1) end)) + and ((sw("^%s*[Tt]?(%d%d):?",seth) and sw("^(%d%d):?",setr) and sw("^(%d%d)",sets) and sw("^(%.%d+)",adds)) + or sw:finish() or (sw"^%s*$" or sw"^%s*[Zz]%s*$" or sw("^%s-([%+%-])(%d%d):?(%d%d)%s*$",setzc) or sw("^%s*([%+%-])(%d%d)%s*$",setzn)) + ) ) + then --print(y,m,d,h,r,s,z,w,u,j) + sw:restart(); y,m,d,h,r,s,z,w,u,j = nil; + repeat -- print(sw:aimchr()) + if sw("^[tT:]?%s*(%d%d?):",seth) then --print("$Time") + _ = sw("^%s*(%d%d?)",setr) and sw("^%s*:%s*(%d%d?)",sets) and sw("^(%.%d+)",adds) + elseif sw("^(%d+)[/\\%s,-]?%s*") then --print("$Digits") + x, c = tonumber(sw[1]), len(sw[1]) + if (x >= 70) or (m and d and (not y)) or (c > 3) then + sety( x + ((x >= 100 or c>3)and 0 or 1900) ) + else + if m then setd(x) else m = x end + end + elseif sw("^(%a+)[/\\%s,-]?%s*") then --print("$Words") + x = sw[1] + if inlist(x, sl_months, 2, sw) then + if m and (not d) and (not y) then d, m = m, false end + setm(mod(sw[0],12)+1) + elseif inlist(x, sl_timezone, 2, sw) then + c = fix(sw[0]) -- ignore gmt and utc + if c ~= 0 then setz(c, x) end + elseif inlist(x, sl_weekdays, 2, sw) then + k = sw[0] + else + sw:back() + -- am pm bce ad ce bc + if sw("^([bB])%s*(%.?)%s*[Cc]%s*(%2)%s*[Ee]%s*(%2)%s*") or sw("^([bB])%s*(%.?)%s*[Cc]%s*(%2)%s*") then + e = e and error_dup() or -1 + elseif sw("^([aA])%s*(%.?)%s*[Dd]%s*(%2)%s*") or sw("^([cC])%s*(%.?)%s*[Ee]%s*(%2)%s*") then + e = e and error_dup() or 1 + elseif sw("^([PApa])%s*(%.?)%s*[Mm]?%s*(%2)%s*") then + x = lwr(sw[1]) -- there should be hour and it must be correct + if (not h) or (h > 12) or (h < 0) then return error_inv() end + if x == 'a' and h == 12 then h = 0 end -- am + if x == 'p' and h ~= 12 then h = h + 12 end -- pm + else error_syn() end + end + elseif not(sw("^([+-])(%d%d?):(%d%d)",setzc) or sw("^([+-])(%d+)",setzn) or sw("^[Zz]%s*$")) then -- sw{"([+-])",{"(%d%d?):(%d%d)","(%d+)"}} + error_syn("?") + end + sw("^%s*") until sw:finish() + --else print("$Iso(Date|Time|Zone)") + end + -- if date is given, it must be complete year, month & day + if (not y and not h) or ((m and not d) or (d and not m)) or ((m and w) or (m and j) or (j and w)) then return error_inv("!") end + -- fix month + if m then m = m - 1 end + -- fix year if we are on BCE + if e and e < 0 and y > 0 then y = 1 - y end + -- create date object + dn = (y and ((w and makedaynum_isoywd(y,w,u)) or (j and makedaynum(y, 0, j)) or makedaynum(y, m, d))) or DAYNUM_DEF + df = makedayfrc(h or 0, r or 0, s or 0, 0) + ((z or 0)*TICKSPERMIN) + --print("Zone",h,r,s,z,m,d,y,df) + return date_new(dn, df) -- no need to :normalize(); + end + local function date_fromtable(v) + local y, m, d = fix(v.year), getmontharg(v.month), fix(v.day) + local h, r, s, t = tonumber(v.hour), tonumber(v.min), tonumber(v.sec), tonumber(v.ticks) + -- atleast there is time or complete date + if (y or m or d) and (not(y and m and d)) then return error("incomplete table") end + return (y or h or r or s or t) and date_new(y and makedaynum(y, m, d) or DAYNUM_DEF, makedayfrc(h or 0, r or 0, s or 0, t or 0)) + end + local tmap = { + ['number'] = function(v) return date_epoch:copy():addseconds(v) end, + ['string'] = function(v) return date_parse(v) end, + ['boolean']= function(v) return date_fromtable(osdate(v and "!*t" or "*t")) end, + ['table'] = function(v) local ref = getmetatable(v) == dobj; return ref and v or date_fromtable(v), ref end + } + local function date_getdobj(v) + local o, r = (tmap[type(v)] or fnil)(v); + return (o and o:normalize() or error"invalid date time value"), r -- if r is true then o is a reference to a date obj + end +--#end -- not DATE_OBJECT_AFX + local function date_from(...) + local arg = pack(...) + local y, m, d = fix(arg[1]), getmontharg(arg[2]), fix(arg[3]) + local h, r, s, t = tonumber(arg[4] or 0), tonumber(arg[5] or 0), tonumber(arg[6] or 0), tonumber(arg[7] or 0) + if y and m and d and h and r and s and t then + return date_new(makedaynum(y, m, d), makedayfrc(h, r, s, t)):normalize() + else + return date_error_arg() + end + end + + --[[ THE DATE OBJECT METHODS ]]-- + function dobj:normalize() + local dn, df = fix(self.daynum), self.dayfrc + self.daynum, self.dayfrc = dn + floor(df/TICKSPERDAY), mod(df, TICKSPERDAY) + return (dn >= DAYNUM_MIN and dn <= DAYNUM_MAX) and self or error("date beyond imposed limits:"..self) + end + + function dobj:getdate() local y, m, d = breakdaynum(self.daynum) return y, m+1, d end + function dobj:gettime() return breakdayfrc(self.dayfrc) end + + function dobj:getclockhour() local h = self:gethours() return h>12 and mod(h,12) or (h==0 and 12 or h) end + + function dobj:getyearday() return yearday(self.daynum) + 1 end + function dobj:getweekday() return weekday(self.daynum) + 1 end -- in lua weekday is sunday = 1, monday = 2 ... + + function dobj:getyear() local r,_,_ = breakdaynum(self.daynum) return r end + function dobj:getmonth() local _,r,_ = breakdaynum(self.daynum) return r+1 end-- in lua month is 1 base + function dobj:getday() local _,_,r = breakdaynum(self.daynum) return r end + function dobj:gethours() return mod(floor(self.dayfrc/TICKSPERHOUR),HOURPERDAY) end + function dobj:getminutes() return mod(floor(self.dayfrc/TICKSPERMIN), MINPERHOUR) end + function dobj:getseconds() return mod(floor(self.dayfrc/TICKSPERSEC ),SECPERMIN) end + function dobj:getfracsec() return mod(floor(self.dayfrc/TICKSPERSEC ),SECPERMIN)+(mod(self.dayfrc,TICKSPERSEC)/TICKSPERSEC) end + function dobj:getticks(u) local x = mod(self.dayfrc,TICKSPERSEC) return u and ((x*u)/TICKSPERSEC) or x end + + function dobj:getweeknumber(wdb) + local wd, yd = weekday(self.daynum), yearday(self.daynum) + if wdb then + wdb = tonumber(wdb) + if wdb then + wd = mod(wd-(wdb-1),7)-- shift the week day base + else + return date_error_arg() + end + end + return (yd < wd and 0) or (floor(yd/7) + ((mod(yd, 7)>=wd) and 1 or 0)) + end + + function dobj:getisoweekday() return mod(weekday(self.daynum)-1,7)+1 end -- sunday = 7, monday = 1 ... + function dobj:getisoweeknumber() return (isowy(self.daynum)) end + function dobj:getisoyear() return isoy(self.daynum) end + function dobj:getisodate() + local w, y = isowy(self.daynum) + return y, w, self:getisoweekday() + end + function dobj:setisoyear(y, w, d) + local cy, cw, cd = self:getisodate() + if y then cy = fix(tonumber(y))end + if w then cw = fix(tonumber(w))end + if d then cd = fix(tonumber(d))end + if cy and cw and cd then + self.daynum = makedaynum_isoywd(cy, cw, cd) + return self:normalize() + else + return date_error_arg() + end + end + + function dobj:setisoweekday(d) return self:setisoyear(nil, nil, d) end + function dobj:setisoweeknumber(w,d) return self:setisoyear(nil, w, d) end + + function dobj:setyear(y, m, d) + local cy, cm, cd = breakdaynum(self.daynum) + if y then cy = fix(tonumber(y))end + if m then cm = getmontharg(m) end + if d then cd = fix(tonumber(d))end + if cy and cm and cd then + self.daynum = makedaynum(cy, cm, cd) + return self:normalize() + else + return date_error_arg() + end + end + + function dobj:setmonth(m, d)return self:setyear(nil, m, d) end + function dobj:setday(d) return self:setyear(nil, nil, d) end + + function dobj:sethours(h, m, s, t) + local ch,cm,cs,ck = breakdayfrc(self.dayfrc) + ch, cm, cs, ck = tonumber(h or ch), tonumber(m or cm), tonumber(s or cs), tonumber(t or ck) + if ch and cm and cs and ck then + self.dayfrc = makedayfrc(ch, cm, cs, ck) + return self:normalize() + else + return date_error_arg() + end + end + + function dobj:setminutes(m,s,t) return self:sethours(nil, m, s, t) end + function dobj:setseconds(s, t) return self:sethours(nil, nil, s, t) end + function dobj:setticks(t) return self:sethours(nil, nil, nil, t) end + + function dobj:spanticks() return (self.daynum*TICKSPERDAY + self.dayfrc) end + function dobj:spanseconds() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERSEC end + function dobj:spanminutes() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERMIN end + function dobj:spanhours() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERHOUR end + function dobj:spandays() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERDAY end + + function dobj:addyears(y, m, d) + local cy, cm, cd = breakdaynum(self.daynum) + if y then y = fix(tonumber(y))else y = 0 end + if m then m = fix(tonumber(m))else m = 0 end + if d then d = fix(tonumber(d))else d = 0 end + if y and m and d then + self.daynum = makedaynum(cy+y, cm+m, cd+d) + return self:normalize() + else + return date_error_arg() + end + end + + function dobj:addmonths(m, d) + return self:addyears(nil, m, d) + end + + local function dobj_adddayfrc(self,n,pt,pd) + n = tonumber(n) + if n then + local x = floor(n/pd); + self.daynum = self.daynum + x; + self.dayfrc = self.dayfrc + (n-x*pd)*pt; + return self:normalize() + else + return date_error_arg() + end + end + function dobj:adddays(n) return dobj_adddayfrc(self,n,TICKSPERDAY,1) end + function dobj:addhours(n) return dobj_adddayfrc(self,n,TICKSPERHOUR,HOURPERDAY) end + function dobj:addminutes(n) return dobj_adddayfrc(self,n,TICKSPERMIN,MINPERDAY) end + function dobj:addseconds(n) return dobj_adddayfrc(self,n,TICKSPERSEC,SECPERDAY) end + function dobj:addticks(n) return dobj_adddayfrc(self,n,1,TICKSPERDAY) end + local tvspec = { + -- Abbreviated weekday name (Sun) + ['%a']=function(self) return sl_weekdays[weekday(self.daynum) + 7] end, + -- Full weekday name (Sunday) + ['%A']=function(self) return sl_weekdays[weekday(self.daynum)] end, + -- Abbreviated month name (Dec) + ['%b']=function(self) return sl_months[self:getmonth() - 1 + 12] end, + -- Full month name (December) + ['%B']=function(self) return sl_months[self:getmonth() - 1] end, + -- Year/100 (19, 20, 30) + ['%C']=function(self) return fmt("%.2d", fix(self:getyear()/100)) end, + -- The day of the month as a number (range 1 - 31) + ['%d']=function(self) return fmt("%.2d", self:getday()) end, + -- year for ISO 8601 week, from 00 (79) + ['%g']=function(self) return fmt("%.2d", mod(self:getisoyear() ,100)) end, + -- year for ISO 8601 week, from 0000 (1979) + ['%G']=function(self) return fmt("%.4d", self:getisoyear()) end, + -- same as %b + ['%h']=function(self) return self:fmt0("%b") end, + -- hour of the 24-hour day, from 00 (06) + ['%H']=function(self) return fmt("%.2d", self:gethours()) end, + -- The hour as a number using a 12-hour clock (01 - 12) + ['%I']=function(self) return fmt("%.2d", self:getclockhour()) end, + -- The day of the year as a number (001 - 366) + ['%j']=function(self) return fmt("%.3d", self:getyearday()) end, + -- Month of the year, from 01 to 12 + ['%m']=function(self) return fmt("%.2d", self:getmonth()) end, + -- Minutes after the hour 55 + ['%M']=function(self) return fmt("%.2d", self:getminutes())end, + -- AM/PM indicator (AM) + ['%p']=function(self) return sl_meridian[self:gethours() > 11 and 1 or -1] end, --AM/PM indicator (AM) + -- The second as a number (59, 20 , 01) + ['%S']=function(self) return fmt("%.2d", self:getseconds()) end, + -- ISO 8601 day of the week, to 7 for Sunday (7, 1) + ['%u']=function(self) return self:getisoweekday() end, + -- Sunday week of the year, from 00 (48) + ['%U']=function(self) return fmt("%.2d", self:getweeknumber()) end, + -- ISO 8601 week of the year, from 01 (48) + ['%V']=function(self) return fmt("%.2d", self:getisoweeknumber()) end, + -- The day of the week as a decimal, Sunday being 0 + ['%w']=function(self) return self:getweekday() - 1 end, + -- Monday week of the year, from 00 (48) + ['%W']=function(self) return fmt("%.2d", self:getweeknumber(2)) end, + -- The year as a number without a century (range 00 to 99) + ['%y']=function(self) return fmt("%.2d", mod(self:getyear() ,100)) end, + -- Year with century (2000, 1914, 0325, 0001) + ['%Y']=function(self) return fmt("%.4d", self:getyear()) end, + -- Time zone offset, the date object is assumed local time (+1000, -0230) + ['%z']=function(self) local b = -self:getbias(); local x = abs(b); return fmt("%s%.4d", b < 0 and "-" or "+", fix(x/60)*100 + floor(mod(x,60))) end, + -- Time zone name, the date object is assumed local time + ['%Z']=function(self) return self:gettzname() end, + -- Misc -- + -- Year, if year is in BCE, prints the BCE Year representation, otherwise result is similar to "%Y" (1 BCE, 40 BCE) + ['%\b']=function(self) local x = self:getyear() return fmt("%.4d%s", x>0 and x or (-x+1), x>0 and "" or " BCE") end, + -- Seconds including fraction (59.998, 01.123) + ['%\f']=function(self) local x = self:getfracsec() return fmt("%s%.9f",x >= 10 and "" or "0", x) end, + -- percent character % + ['%%']=function(self) return "%" end, + -- Group Spec -- + -- 12-hour time, from 01:00:00 AM (06:55:15 AM); same as "%I:%M:%S %p" + ['%r']=function(self) return self:fmt0("%I:%M:%S %p") end, + -- hour:minute, from 01:00 (06:55); same as "%I:%M" + ['%R']=function(self) return self:fmt0("%I:%M") end, + -- 24-hour time, from 00:00:00 (06:55:15); same as "%H:%M:%S" + ['%T']=function(self) return self:fmt0("%H:%M:%S") end, + -- month/day/year from 01/01/00 (12/02/79); same as "%m/%d/%y" + ['%D']=function(self) return self:fmt0("%m/%d/%y") end, + -- year-month-day (1979-12-02); same as "%Y-%m-%d" + ['%F']=function(self) return self:fmt0("%Y-%m-%d") end, + -- The preferred date and time representation; same as "%x %X" + ['%c']=function(self) return self:fmt0("%x %X") end, + -- The preferred date representation, same as "%a %b %d %\b" + ['%x']=function(self) return self:fmt0("%a %b %d %\b") end, + -- The preferred time representation, same as "%H:%M:%\f" + ['%X']=function(self) return self:fmt0("%H:%M:%\f") end, + -- GroupSpec -- + -- Iso format, same as "%Y-%m-%dT%T" + ['${iso}'] = function(self) return self:fmt0("%Y-%m-%dT%T") end, + -- http format, same as "%a, %d %b %Y %T GMT" + ['${http}'] = function(self) return self:fmt0("%a, %d %b %Y %T GMT") end, + -- ctime format, same as "%a %b %d %T GMT %Y" + ['${ctime}'] = function(self) return self:fmt0("%a %b %d %T GMT %Y") end, + -- RFC850 format, same as "%A, %d-%b-%y %T GMT" + ['${rfc850}'] = function(self) return self:fmt0("%A, %d-%b-%y %T GMT") end, + -- RFC1123 format, same as "%a, %d %b %Y %T GMT" + ['${rfc1123}'] = function(self) return self:fmt0("%a, %d %b %Y %T GMT") end, + -- asctime format, same as "%a %b %d %T %Y" + ['${asctime}'] = function(self) return self:fmt0("%a %b %d %T %Y") end, + } + function dobj:fmt0(str) return (gsub(str, "%%[%a%%\b\f]", function(x) local f = tvspec[x];return (f and f(self)) or x end)) end + function dobj:fmt(str) + str = str or self.fmtstr or fmtstr + return self:fmt0((gmatch(str, "${%w+}")) and (gsub(str, "${%w+}", function(x)local f=tvspec[x];return (f and f(self)) or x end)) or str) + end + + function dobj.__lt(a, b) if (a.daynum == b.daynum) then return (a.dayfrc < b.dayfrc) else return (a.daynum < b.daynum) end end + function dobj.__le(a, b) if (a.daynum == b.daynum) then return (a.dayfrc <= b.dayfrc) else return (a.daynum <= b.daynum) end end + function dobj.__eq(a, b)return (a.daynum == b.daynum) and (a.dayfrc == b.dayfrc) end + function dobj.__sub(a,b) + local d1, d2 = date_getdobj(a), date_getdobj(b) + local d0 = d1 and d2 and date_new(d1.daynum - d2.daynum, d1.dayfrc - d2.dayfrc) + return d0 and d0:normalize() + end + function dobj.__add(a,b) + local d1, d2 = date_getdobj(a), date_getdobj(b) + local d0 = d1 and d2 and date_new(d1.daynum + d2.daynum, d1.dayfrc + d2.dayfrc) + return d0 and d0:normalize() + end + function dobj.__concat(a, b) return tostring(a) .. tostring(b) end + function dobj:__tostring() return self:fmt() end + + function dobj:copy() return date_new(self.daynum, self.dayfrc) end + +--[[ THE LOCAL DATE OBJECT METHODS ]]-- + function dobj:tolocal() + local dn,df = self.daynum, self.dayfrc + local bias = getbiasutc2(self) + if bias then + -- utc = local + bias; local = utc - bias + self.daynum = dn + self.dayfrc = df - bias*TICKSPERSEC + return self:normalize() + else + return nil + end + end + + function dobj:toutc() + local dn,df = self.daynum, self.dayfrc + local bias = getbiasloc2(dn, df) + if bias then + -- utc = local + bias; + self.daynum = dn + self.dayfrc = df + bias*TICKSPERSEC + return self:normalize() + else + return nil + end + end + + function dobj:getbias() return (getbiasloc2(self.daynum, self.dayfrc))/SECPERMIN end + + function dobj:gettzname() + local _, tvu, _ = getbiasloc2(self.daynum, self.dayfrc) + return tvu and osdate("%Z",tvu) or "" + end + +--#if not DATE_OBJECT_AFX then + function date.time(h, r, s, t) + h, r, s, t = tonumber(h or 0), tonumber(r or 0), tonumber(s or 0), tonumber(t or 0) + if h and r and s and t then + return date_new(DAYNUM_DEF, makedayfrc(h, r, s, t)) + else + return date_error_arg() + end + end + + function date:__call(...) + local arg = pack(...) + if arg.n > 1 then return (date_from(...)) + elseif arg.n == 0 then return (date_getdobj(false)) + else local o, r = date_getdobj(arg[1]); return r and o:copy() or o end + end + + date.diff = dobj.__sub + + function date.isleapyear(v) + local y = fix(v); + if not y then + y = date_getdobj(v) + y = y and y:getyear() + end + return isleapyear(y+0) + end + + function date.epoch() return date_epoch:copy() end + + function date.isodate(y,w,d) return date_new(makedaynum_isoywd(y + 0, w and (w+0) or 1, d and (d+0) or 1), 0) end + +-- Internal functions + function date.fmt(str) if str then fmtstr = str end; return fmtstr end + function date.daynummin(n) DAYNUM_MIN = (n and n < DAYNUM_MAX) and n or DAYNUM_MIN return n and DAYNUM_MIN or date_new(DAYNUM_MIN, 0):normalize()end + function date.daynummax(n) DAYNUM_MAX = (n and n > DAYNUM_MIN) and n or DAYNUM_MAX return n and DAYNUM_MAX or date_new(DAYNUM_MAX, 0):normalize()end + function date.ticks(t) if t then setticks(t) end return TICKSPERSEC end +--#end -- not DATE_OBJECT_AFX + + local tm = osdate("!*t", 0); + if tm then + date_epoch = date_new(makedaynum(tm.year, tm.month - 1, tm.day), makedayfrc(tm.hour, tm.min, tm.sec, 0)) + -- the distance from our epoch to os epoch in daynum + DATE_EPOCH = date_epoch and date_epoch:spandays() + else -- error will be raise only if called! + date_epoch = setmetatable({},{__index = function() error("failed to get the epoch date") end}) + end + +--#if not DATE_OBJECT_AFX then +return date +--#else +--$return date_from +--#end diff --git a/website/backend/app/libs/db.lua b/website/backend/app/libs/db.lua new file mode 100755 index 0000000..43d8069 --- /dev/null +++ b/website/backend/app/libs/db.lua @@ -0,0 +1,135 @@ +local sgsub = string.gsub +local tinsert = table.insert +local type = type +local ipairs = ipairs +local pairs = pairs +local mysql = require("resty.mysql") +local cjson = require("cjson") +local utils = require("app.libs.utils") +local config = require("app.config.config") +local DB = {} + +function DB:new(conf) + conf = conf or config.mysql + local instance = {} + instance.conf = conf + setmetatable(instance, { __index = self}) + return instance +end + +function DB:exec(sql) + if not sql then + ngx.log(ngx.ERR, "sql parse error! please check") + return nil, "sql parse error! please check" + end + + local conf = self.conf + local db, err = mysql:new() + if not db then + ngx.say("failed to instantiate mysql: ", err) + return + end + db:set_timeout(conf.timeout) -- 1 sec + + local ok, err, errno, sqlstate = db:connect(conf.connect_config) + if not ok then + ngx.say("failed to connect: ", err, ": ", errno, " ", sqlstate) + return + end + + ngx.log(ngx.ERR, "connected to mysql, reused_times:", db:get_reused_times(), " sql:", sql) + + db:query("SET NAMES utf8") + local res, err, errno, sqlstate = db:query(sql) + if not res then + ngx.log(ngx.ERR, "bad result: ", err, ": ", errno, ": ", sqlstate, ".") + end + + local ok, err = db:set_keepalive(conf.pool_config.max_idle_timeout, conf.pool_config.pool_size) + if not ok then + ngx.say("failed to set keepalive: ", err) + end + + return res, err, errno, sqlstate +end + + + +function DB:query(sql, params) + sql = self:parse_sql(sql, params) + return self:exec(sql) +end + +function DB:select(sql, params) + return self:query(sql, params) +end + +function DB:insert(sql, params) + local res, err, errno, sqlstate = self:query(sql, params) + if res and not err then + return res.insert_id, err + else + return res, err + end +end + +function DB:update(sql, params) + return self:query(sql, params) +end + +function DB:delete(sql, params) + local res, err, errno, sqlstate = self:query(sql, params) + if res and not err then + return res.affected_rows, err + else + return res, err + end +end + +local function split(str, delimiter) + if str==nil or str=='' or delimiter==nil then + return nil + end + + local result = {} + for match in (str..delimiter):gmatch("(.-)"..delimiter) do + tinsert(result, match) + end + return result +end + + +local function compose(t, params) + if t==nil or params==nil or type(t)~="table" or type(params)~="table" or #t~=#params+1 or #t==0 then + return nil + else + local result = t[1] + for i=1, #params do + result = result .. params[i].. t[i+1] + end + return result + end +end + + +function DB:parse_sql(sql, params) + if not params or not utils.table_is_array(params) or #params == 0 then + return sql + end + + local new_params = {} + for i, v in ipairs(params) do + if v and type(v) == "string" then + v = ngx.quote_sql_str(v) + end + + tinsert(new_params, v) + end + + local t = split(sql,"?") + local sql = compose(t, new_params) + + return sql +end + +return DB diff --git a/website/backend/app/libs/inspect.lua b/website/backend/app/libs/inspect.lua new file mode 100755 index 0000000..e2e3806 --- /dev/null +++ b/website/backend/app/libs/inspect.lua @@ -0,0 +1,334 @@ +local inspect ={ + _VERSION = 'inspect.lua 3.1.0', + _URL = 'http://github.com/kikito/inspect.lua', + _DESCRIPTION = 'human-readable representations of tables', + _LICENSE = [[ + MIT LICENSE + + Copyright (c) 2013 Enrique García Cota + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ]] +} + +local tostring = tostring + +inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end}) +inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end}) + +local function rawpairs(t) + return next, t, nil +end + +-- Apostrophizes the string if it has quotes, but not aphostrophes +-- Otherwise, it returns a regular quoted string +local function smartQuote(str) + if str:match('"') and not str:match("'") then + return "'" .. str .. "'" + end + return '"' .. str:gsub('"', '\\"') .. '"' +end + +-- \a => '\\a', \0 => '\\0', 31 => '\31' +local shortControlCharEscapes = { + ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n", + ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v" +} +local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031 +for i=0, 31 do + local ch = string.char(i) + if not shortControlCharEscapes[ch] then + shortControlCharEscapes[ch] = "\\"..i + longControlCharEscapes[ch] = string.format("\\%03d", i) + end +end + +local function escape(str) + return (str:gsub("\\", "\\\\") + :gsub("(%c)%f[0-9]", longControlCharEscapes) + :gsub("%c", shortControlCharEscapes)) +end + +local function isIdentifier(str) + return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" ) +end + +local function isSequenceKey(k, sequenceLength) + return type(k) == 'number' + and 1 <= k + and k <= sequenceLength + and math.floor(k) == k +end + +local defaultTypeOrders = { + ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4, + ['function'] = 5, ['userdata'] = 6, ['thread'] = 7 +} + +local function sortKeys(a, b) + local ta, tb = type(a), type(b) + + -- strings and numbers are sorted numerically/alphabetically + if ta == tb and (ta == 'string' or ta == 'number') then return a < b end + + local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb] + -- Two default types are compared according to the defaultTypeOrders table + if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb] + elseif dta then return true -- default types before custom ones + elseif dtb then return false -- custom types after default ones + end + + -- custom types are sorted out alphabetically + return ta < tb +end + +-- For implementation reasons, the behavior of rawlen & # is "undefined" when +-- tables aren't pure sequences. So we implement our own # operator. +local function getSequenceLength(t) + local len = 1 + local v = rawget(t,len) + while v ~= nil do + len = len + 1 + v = rawget(t,len) + end + return len - 1 +end + +local function getNonSequentialKeys(t) + local keys, keysLength = {}, 0 + local sequenceLength = getSequenceLength(t) + for k,_ in rawpairs(t) do + if not isSequenceKey(k, sequenceLength) then + keysLength = keysLength + 1 + keys[keysLength] = k + end + end + table.sort(keys, sortKeys) + return keys, keysLength, sequenceLength +end + +local function countTableAppearances(t, tableAppearances) + tableAppearances = tableAppearances or {} + + if type(t) == 'table' then + if not tableAppearances[t] then + tableAppearances[t] = 1 + for k,v in rawpairs(t) do + countTableAppearances(k, tableAppearances) + countTableAppearances(v, tableAppearances) + end + countTableAppearances(getmetatable(t), tableAppearances) + else + tableAppearances[t] = tableAppearances[t] + 1 + end + end + + return tableAppearances +end + +local copySequence = function(s) + local copy, len = {}, #s + for i=1, len do copy[i] = s[i] end + return copy, len +end + +local function makePath(path, ...) + local keys = {...} + local newPath, len = copySequence(path) + for i=1, #keys do + newPath[len + i] = keys[i] + end + return newPath +end + +local function processRecursive(process, item, path, visited) + if item == nil then return nil end + if visited[item] then return visited[item] end + + local processed = process(item, path) + if type(processed) == 'table' then + local processedCopy = {} + visited[item] = processedCopy + local processedKey + + for k,v in rawpairs(processed) do + processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited) + if processedKey ~= nil then + processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited) + end + end + + local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited) + if type(mt) ~= 'table' then mt = nil end -- ignore not nil/table __metatable field + setmetatable(processedCopy, mt) + processed = processedCopy + end + return processed +end + + + +------------------------------------------------------------------- + +local Inspector = {} +local Inspector_mt = {__index = Inspector} + +function Inspector:puts(...) + local args = {...} + local buffer = self.buffer + local len = #buffer + for i=1, #args do + len = len + 1 + buffer[len] = args[i] + end +end + +function Inspector:down(f) + self.level = self.level + 1 + f() + self.level = self.level - 1 +end + +function Inspector:tabify() + self:puts(self.newline, string.rep(self.indent, self.level)) +end + +function Inspector:alreadyVisited(v) + return self.ids[v] ~= nil +end + +function Inspector:getId(v) + local id = self.ids[v] + if not id then + local tv = type(v) + id = (self.maxIds[tv] or 0) + 1 + self.maxIds[tv] = id + self.ids[v] = id + end + return tostring(id) +end + +function Inspector:putKey(k) + if isIdentifier(k) then return self:puts(k) end + self:puts("[") + self:putValue(k) + self:puts("]") +end + +function Inspector:putTable(t) + if t == inspect.KEY or t == inspect.METATABLE then + self:puts(tostring(t)) + elseif self:alreadyVisited(t) then + self:puts('
') + elseif self.level >= self.depth then + self:puts('{...}') + else + if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end + + local nonSequentialKeys, nonSequentialKeysLength, sequenceLength = getNonSequentialKeys(t) + local mt = getmetatable(t) + + self:puts('{') + self:down(function() + local count = 0 + for i=1, sequenceLength do + if count > 0 then self:puts(',') end + self:puts(' ') + self:putValue(t[i]) + count = count + 1 + end + + for i=1, nonSequentialKeysLength do + local k = nonSequentialKeys[i] + if count > 0 then self:puts(',') end + self:tabify() + self:putKey(k) + self:puts(' = ') + self:putValue(t[k]) + count = count + 1 + end + + if type(mt) == 'table' then + if count > 0 then self:puts(',') end + self:tabify() + self:puts(' = ') + self:putValue(mt) + end + end) + + if nonSequentialKeysLength > 0 or type(mt) == 'table' then -- result is multi-lined. Justify closing } + self:tabify() + elseif sequenceLength > 0 then -- array tables have one extra space before closing } + self:puts(' ') + end + + self:puts('}') + end +end + +function Inspector:putValue(v) + local tv = type(v) + + if tv == 'string' then + self:puts(smartQuote(escape(v))) + elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or + tv == 'cdata' or tv == 'ctype' then + self:puts(tostring(v)) + elseif tv == 'table' then + self:putTable(v) + else + self:puts('<', tv, ' ', self:getId(v), '>') + end +end + +------------------------------------------------------------------- + +function inspect.inspect(root, options) + options = options or {} + + local depth = options.depth or math.huge + local newline = options.newline or '\n' + local indent = options.indent or ' ' + local process = options.process + + if process then + root = processRecursive(process, root, {}, {}) + end + + local inspector = setmetatable({ + depth = depth, + level = 0, + buffer = {}, + ids = {}, + maxIds = {}, + newline = newline, + indent = indent, + tableAppearances = countTableAppearances(root) + }, Inspector_mt) + + inspector:putValue(root) + + return table.concat(inspector.buffer) +end + +setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end }) + +return inspect + diff --git a/website/backend/app/libs/ip_location.lua b/website/backend/app/libs/ip_location.lua new file mode 100755 index 0000000..8639691 --- /dev/null +++ b/website/backend/app/libs/ip_location.lua @@ -0,0 +1,281 @@ +#!/usr/bin/env lua +local ngx = require('ngx') + +local setmetatable = setmetatable +local byte = string.byte +local match = string.match +local rawget = rawget +local cjson = require "cjson" + +local ok, new_tab = pcall(require, "table.new") +if not ok or type(new_tab) ~= "function" then + new_tab = function(narr, nrec) return {} end +end + +local _M = new_tab(0, 54) + +local mt = { __index = _M } + +_M._VERSION = '0.01' + +-- 测试TOKEN 及 地址 +local token = "cc87f3c77747bccbaaee35006da1ebb65e0bad57" +local ipIpUrl = "http://freeapi.ipip.net/" + +-- 切割字符串 +local function split(s, p) + local rt = {} + string.gsub(s, '[^' .. p .. ']+', function(w) table.insert(rt, w) end) + return rt +end + +-- 拓展 +string.split = function(str, pattern) + pattern = pattern or "[^%s]+" + if pattern:len() == 0 then pattern = "[^%s]+" end + local parts = { __index = table.insert } + setmetatable(parts, parts) + str:gsub(pattern, parts) + setmetatable(parts, nil) + parts.__index = nil + return parts +end + + +-- 转成整型 +local function bit32lshift(b, disp) + return (b * 2 ^ disp) % 2 ^ 32 +end + +-- 转换ip +local function byteToUint32(a, b, c, d) + local _int = 0 + if a then + _int = _int + bit32lshift(a, 24) + end + _int = _int + bit32lshift(b, 16) + _int = _int + bit32lshift(c, 8) + _int = _int + d + if _int >= 0 then + return _int + else + return _int + math.pow(2, 32) + end +end + +-- 返回数据模版 +local response_template = { + "country", -- // 国家 + "city", -- // 省会或直辖市(国内) + "region", -- // 地区或城市 (国内) + "place", -- // 学校或单位 (国内) + "operator", -- // 运营商字段(只有购买了带有运营商版本的数据库才会有) + "latitude", -- // 纬度 (每日版本提供) + "longitude", -- // 经度 (每日版本提供) + "timeZone", -- // 时区一, 可能不存在 (每日版本提供) + "timeZoneCode", -- // 时区二, 可能不存在 (每日版本提供) + "administrativeAreaCode", -- // 中国行政区划代码 (每日版本提供) + "internationalPhoneCode", -- // 国际电话代码 (每日版本提供) + "countryTwoDigitCode", -- // 国家二位代码 (每日版本提供) + "worldContinentCode" -- // 世界大洲代码 (每日版本提供) +} + +-- 转成 table类型 +local function toTable(location) + local response = {} + for k, v in ipairs(location) do + response[response_template[k]] = v + end + + return response +end + +-- 发送请求 +local function sendRequest(url, method, body, headers) + local http = require "resty.http" + local httpc = http.new() + local res, err = httpc:request_uri(url, { + method = method, + body = body, + headers = headers + }) + + if not res then + ngx.log(ngx.ERR, "failed to request: " .. err .. url) + return nil, err + end + + if 200 ~= res.status then + ngx.log(ngx.ERR, res.status) + return nil, res.status + end + + return res.body +end + +-- 初始化 +function _M.new(self, address, token) + return setmetatable({ _ipAddress = address, _token = token, _ipBinaryFilePath = require("config.app").ip_binary_file_path }, mt) +end + + +-- 从文件获取地区信息 +function _M.ipLocation(self, ipstr) + local ipBinaryFilePath = rawget(self, "_ipBinaryFilePath") + if not ipBinaryFilePath then + ngx.log(ngx.ERR, ipBinaryFilePath) + return nil, " file ptah not initialized" + end + + local ip1, ip2, ip3, ip4 = match(ipstr, "(%d+).(%d+).(%d+).(%d+)") + local ip_uint32 = byteToUint32(ip1, ip2, ip3, ip4) + local file = io.open(ipBinaryFilePath) + if file == nil then + return nil + end + + local str = file:read(4) + local offset_len = byteToUint32(byte(str, 1), byte(str, 2), byte(str, 3), byte(str, 4)) + + local indexBuffer = file:read(offset_len - 4) + + local tmp_offset = ip1 * 4 + local start_len = byteToUint32(byte(indexBuffer, tmp_offset + 4), byte(indexBuffer, tmp_offset + 3), byte(indexBuffer, tmp_offset + 2), byte(indexBuffer, tmp_offset + 1)) + + local max_comp_len = offset_len - 1028 + local start = start_len * 8 + 1024 + 1 + local index_offset = -1 + local index_length = -1 + while start < max_comp_len do + local find_uint32 = byteToUint32(byte(indexBuffer, start), byte(indexBuffer, start + 1), byte(indexBuffer, start + 2), byte(indexBuffer, start + 3)) + if ip_uint32 <= find_uint32 then + index_offset = byteToUint32(0, byte(indexBuffer, start + 6), byte(indexBuffer, start + 5), byte(indexBuffer, start + 4)) + index_length = byte(indexBuffer, start + 7) + break + end + start = start + 8 + end + + if index_offset == -1 or index_length == -1 then + return nil + end + + local offset = offset_len + index_offset - 1024 + + file:seek("set", offset) + + return file:read(index_length) +end + +-- 获取所有信息 +function _M.location(self) + local ipAddress = rawget(self, "_ipAddress") + if not ipAddress then + return nil, "not initialized" + end + + local address = self:ipLocation(ipAddress) + if not address then + ngx.log(ngx.ERR, { "ip address data nil" }) + return nil, "ip address data nil" + end + + if type(address) == "string" then + return toTable(split(address, "%s+")) + end + + return address +end + +-- 通过api获取 +function _M.locationApi(self, sid, uid) + local ipAddress = rawget(self, "_ipAddress") + if not ipAddress then + return nil, "not initialized" + end + + local _token = rawget(self, "_token") + + local myToken = (_token and _token) or token + + local sign, err = ngx.md5("addr=" .. ipAddress .. "&token=" .. myToken) + + if not sign then + return nil, err + end + + local url = ipIpUrl .. "find" + + local headers = { + ["Token"] = myToken + } + + local params = "addr=" .. ipAddress .. "&sid=" .. sid .. "&uid=" .. uid .. "&sig=" .. sign + + local body, err = sendRequest(url, "GET", params, headers) + + if not body or #body < 1 then + return nil, err + end + + -- local body = [[{"ret":"ok","data":["中国","天津","天津","","鹏博士","39.128399","117.185112","Asia/Shanghai","UTC+8","120000","86","CN","AP"]}]] + local response = cjson.decode(body) + + if not response.data then + return response + end + + return toTable(response.data) +end + +-- 通过免费的api获取 +function _M.locationApiFree(self) + local ipAddress = rawget(self, "_ipAddress") + if not ipAddress then + return nil, "not initialized" + end + + local url = ipIpUrl .. ipAddress + + local headers = { + ["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + ["Cache-Control"] = "no-cache", + ["Connection"] = "keep-alive", + ["User-Agent"] = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36", + ["Content-Type"] = "application/x-www-form-urlencoded", + } + + local body, err = sendRequest(url, "GET", "", headers) + + if not body then + return nil, err + end + + return toTable(cjson.decode(body)) +end + +-- 获取当前可访问状态 +function _M.apiStatus(self, token) + if not token then + local token = rawget(self, "_token") + if not token then + return nil, "not initialized" + end + end + + local url = ipIpUrl .. "find_status" + + local headers = { + ["Token"] = token + } + + local body, err = sendRequest(url, "GET", "", headers) + + if not body then + return nil, err + end + + return cjson.decode(body) +end + +return _M \ No newline at end of file diff --git a/website/backend/app/libs/log_api.lua b/website/backend/app/libs/log_api.lua new file mode 100755 index 0000000..8c562fb --- /dev/null +++ b/website/backend/app/libs/log_api.lua @@ -0,0 +1,49 @@ +local table = table + +-- 日志级别 +local log_level = { + LOG_DEFAULT = 1, + LOG_TRACE = 1, + LOG_DEBUG = 2, + LOG_INFO = 3, + LOG_WARN = 4, + LOG_ERROR = 5, + LOG_FATAL = 6, +} + +local defaultLevel = log_level.LOG_DEBUG + +-- 错误日志 -- +local function logger(str, level, color) + return function (...) + if level >= defaultLevel then + local info = table.pack(...) + info[#info+1] = "\n" + info[#info+1] = "\x1b[0m" + ngx.log(ngx.ERR, string.format("%s%s", color, str), table.unpack(info)) + end + end +end + +local M = { + TRACE = logger("[trace]", log_level.LOG_TRACE, "\x1b[35m"), + DEBUG = logger("[debug]", log_level.LOG_DEBUG, "\x1b[32m"), + INFO = logger("[info]", log_level.LOG_INFO, "\x1b[34m"), + WARN = logger("[warning]", log_level.LOG_WARN, "\x1b[33m"), + ERROR = logger("[error]", log_level.LOG_ERROR, "\x1b[31m"), + FATAL = logger("[fatal]", log_level.LOG_FATAL,"\x1b[31m") +} + +-- 错误日志 -- + +setmetatable(M, { + __call = function(t) + for k, v in pairs(t) do + _G[k] = v + end + end, +}) + +M() + +return M diff --git a/website/backend/app/libs/random.lua b/website/backend/app/libs/random.lua new file mode 100755 index 0000000..96f3961 --- /dev/null +++ b/website/backend/app/libs/random.lua @@ -0,0 +1,86 @@ +local require = require +local ffi = require "ffi" +local ffi_cdef = ffi.cdef +local ffi_new = ffi.new +local ffi_str = ffi.string +local ffi_typeof = ffi.typeof +local C = ffi.C +local type = type +local random = math.random +local randomseed = math.randomseed +local concat = table.concat +local tostring = tostring +local pcall = pcall + +ffi_cdef[[ +typedef unsigned char u_char; +u_char * ngx_hex_dump(u_char *dst, const u_char *src, size_t len); +int RAND_bytes(u_char *buf, int num); +]] + +local ok, new_tab = pcall(require, "table.new") +if not ok then + new_tab = function () return {} end +end + +local alnum = { + 'A','B','C','D','E','F','G','H','I','J','K','L','M', + 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + 'a','b','c','d','e','f','g','h','i','j','k','l','m', + 'n','o','p','q','r','s','t','u','v','w','x','y','z', + '0','1','2','3','4','5','6','7','8','9' +} + +local t = ffi_typeof "uint8_t[?]" + +local function bytes(len, format) + local s = ffi_new(t, len) + C.RAND_bytes(s, len) + if not s then return nil end + if format == "hex" then + local b = ffi_new(t, len * 2) + C.ngx_hex_dump(b, s, len) + return ffi_str(b, len * 2), true + else + return ffi_str(s, len), true + end +end + +local function seed() + local a,b,c,d = bytes(4):byte(1, 4) + return randomseed(a * 0x1000000 + b * 0x10000 + c * 0x100 + d) +end + +local function number(min, max, reseed) + if reseed then seed() end + if min and max then return random(min, max) + elseif min then return random(min) + else return random() end +end + +local function token(len, chars, sep) + chars = chars or alnum + local count + local token = new_tab(len, 0) + if type(chars) ~= "table" then + chars = tostring(chars) + count = #chars + local n + for i=1,len do + n = number(1, count) + token[i] = chars:sub(n, n) + end + else + count = #chars + for i=1,len do token[i] = chars[number(1, count)] end + end + return concat(token, sep) +end + +seed() + +return { + bytes = bytes, + number = number, + token = token +} \ No newline at end of file diff --git a/website/backend/app/libs/resty/http/http.lua b/website/backend/app/libs/resty/http/http.lua new file mode 100755 index 0000000..8655601 --- /dev/null +++ b/website/backend/app/libs/resty/http/http.lua @@ -0,0 +1,1064 @@ +local http_headers = require "resty.http.http_headers" + +local ngx = ngx +local ngx_socket_tcp = ngx.socket.tcp +local ngx_req = ngx.req +local ngx_req_socket = ngx_req.socket +local ngx_req_get_headers = ngx_req.get_headers +local ngx_req_get_method = ngx_req.get_method +local str_gmatch = string.gmatch +local str_lower = string.lower +local str_upper = string.upper +local str_find = string.find +local str_sub = string.sub +local tbl_concat = table.concat +local tbl_insert = table.insert +local ngx_encode_args = ngx.encode_args +local ngx_re_match = ngx.re.match +local ngx_re_gmatch = ngx.re.gmatch +local ngx_re_sub = ngx.re.sub +local ngx_re_gsub = ngx.re.gsub +local ngx_re_find = ngx.re.find +local ngx_log = ngx.log +local ngx_DEBUG = ngx.DEBUG +local ngx_ERR = ngx.ERR +local ngx_var = ngx.var +local ngx_print = ngx.print +local ngx_header = ngx.header +local co_yield = coroutine.yield +local co_create = coroutine.create +local co_status = coroutine.status +local co_resume = coroutine.resume +local setmetatable = setmetatable +local tonumber = tonumber +local tostring = tostring +local unpack = unpack +local rawget = rawget +local select = select +local ipairs = ipairs +local pairs = pairs +local pcall = pcall +local type = type + + +-- http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1 +local HOP_BY_HOP_HEADERS = { + ["connection"] = true, + ["keep-alive"] = true, + ["proxy-authenticate"] = true, + ["proxy-authorization"] = true, + ["te"] = true, + ["trailers"] = true, + ["transfer-encoding"] = true, + ["upgrade"] = true, + ["content-length"] = true, -- Not strictly hop-by-hop, but Nginx will deal + -- with this (may send chunked for example). +} + + +-- Reimplemented coroutine.wrap, returning "nil, err" if the coroutine cannot +-- be resumed. This protects user code from inifite loops when doing things like +-- repeat +-- local chunk, err = res.body_reader() +-- if chunk then -- <-- This could be a string msg in the core wrap function. +-- ... +-- end +-- until not chunk +local co_wrap = function(func) + local co = co_create(func) + if not co then + return nil, "could not create coroutine" + else + return function(...) + if co_status(co) == "suspended" then + return select(2, co_resume(co, ...)) + else + return nil, "can't resume a " .. co_status(co) .. " coroutine" + end + end + end +end + + +-- Returns a new table, recursively copied from the one given. +-- +-- @param table table to be copied +-- @return table +local function tbl_copy(orig) + local orig_type = type(orig) + local copy + if orig_type == "table" then + copy = {} + for orig_key, orig_value in next, orig, nil do + copy[tbl_copy(orig_key)] = tbl_copy(orig_value) + end + else -- number, string, boolean, etc + copy = orig + end + return copy +end + + +local _M = { + _VERSION = '0.12', +} +_M._USER_AGENT = "lua-resty-http/" .. _M._VERSION .. " (Lua) ngx_lua/" .. ngx.config.ngx_lua_version + +local mt = { __index = _M } + + +local HTTP = { + [1.0] = " HTTP/1.0\r\n", + [1.1] = " HTTP/1.1\r\n", +} + +local DEFAULT_PARAMS = { + method = "GET", + path = "/", + version = 1.1, +} + + +function _M.new(self) + local sock, err = ngx_socket_tcp() + if not sock then + return nil, err + end + return setmetatable({ sock = sock, keepalive = true }, mt) +end + + +function _M.set_timeout(self, timeout) + local sock = self.sock + if not sock then + return nil, "not initialized" + end + + return sock:settimeout(timeout) +end + + +function _M.set_timeouts(self, connect_timeout, send_timeout, read_timeout) + local sock = self.sock + if not sock then + return nil, "not initialized" + end + + return sock:settimeouts(connect_timeout, send_timeout, read_timeout) +end + + +function _M.ssl_handshake(self, ...) + local sock = self.sock + if not sock then + return nil, "not initialized" + end + + self.ssl = true + + return sock:sslhandshake(...) +end + + +function _M.connect(self, ...) + local sock = self.sock + if not sock then + return nil, "not initialized" + end + + self.host = select(1, ...) + self.port = select(2, ...) + + -- If port is not a number, this is likely a unix domain socket connection. + if type(self.port) ~= "number" then + self.port = nil + end + + self.keepalive = true + + return sock:connect(...) +end + + +function _M.set_keepalive(self, ...) + local sock = self.sock + if not sock then + return nil, "not initialized" + end + + if self.keepalive == true then + return sock:setkeepalive(...) + else + -- The server said we must close the connection, so we cannot setkeepalive. + -- If close() succeeds we return 2 instead of 1, to differentiate between + -- a normal setkeepalive() failure and an intentional close(). + local res, err = sock:close() + if res then + return 2, "connection must be closed" + else + return res, err + end + end +end + + +function _M.get_reused_times(self) + local sock = self.sock + if not sock then + return nil, "not initialized" + end + + return sock:getreusedtimes() +end + + +function _M.close(self) + local sock = self.sock + if not sock then + return nil, "not initialized" + end + + return sock:close() +end + + +local function _should_receive_body(method, code) + if method == "HEAD" then return nil end + if code == 204 or code == 304 then return nil end + if code >= 100 and code < 200 then return nil end + return true +end + + +function _M.parse_uri(self, uri, query_in_path) + if query_in_path == nil then query_in_path = true end + + local m, err = ngx_re_match(uri, [[^(?:(http[s]?):)?//([^:/\?]+)(?::(\d+))?([^\?]*)\??(.*)]], "jo") + + if not m then + if err then + return nil, "failed to match the uri: " .. uri .. ", " .. err + end + + return nil, "bad uri: " .. uri + else + -- If the URI is schemaless (i.e. //example.com) try to use our current + -- request scheme. + if not m[1] then + local scheme = ngx_var.scheme + if scheme == "http" or scheme == "https" then + m[1] = scheme + else + return nil, "schemaless URIs require a request context: " .. uri + end + end + + if m[3] then + m[3] = tonumber(m[3]) + else + if m[1] == "https" then + m[3] = 443 + else + m[3] = 80 + end + end + if not m[4] or "" == m[4] then m[4] = "/" end + + if query_in_path and m[5] and m[5] ~= "" then + m[4] = m[4] .. "?" .. m[5] + m[5] = nil + end + + return m, nil + end +end + + +local function _format_request(params) + local version = params.version + local headers = params.headers or {} + + local query = params.query or "" + if type(query) == "table" then + query = "?" .. ngx_encode_args(query) + elseif query ~= "" and str_sub(query, 1, 1) ~= "?" then + query = "?" .. query + end + + -- Initialize request + local req = { + str_upper(params.method), + " ", + params.path, + query, + HTTP[version], + -- Pre-allocate slots for minimum headers and carriage return. + true, + true, + true, + } + local c = 6 -- req table index it's faster to do this inline vs table.insert + + -- Append headers + for key, values in pairs(headers) do + if type(values) ~= "table" then + values = {values} + end + + key = tostring(key) + for _, value in pairs(values) do + req[c] = key .. ": " .. tostring(value) .. "\r\n" + c = c + 1 + end + end + + -- Close headers + req[c] = "\r\n" + + return tbl_concat(req) +end + + +local function _receive_status(sock) + local line, err = sock:receive("*l") + if not line then + return nil, nil, nil, err + end + + return tonumber(str_sub(line, 10, 12)), tonumber(str_sub(line, 6, 8)), str_sub(line, 14) +end + + +local function _receive_headers(sock) + local headers = http_headers.new() + + repeat + local line, err = sock:receive("*l") + if not line then + return nil, err + end + + local m, err = ngx_re_match(line, "([^:\\s]+):\\s*(.+)", "jo") + if not m then + break + end + + local key = m[1] + local val = m[2] + if headers[key] then + if type(headers[key]) ~= "table" then + headers[key] = { headers[key] } + end + tbl_insert(headers[key], tostring(val)) + else + headers[key] = tostring(val) + end + until ngx_re_find(line, "^\\s*$", "jo") + + return headers, nil +end + + +local function _chunked_body_reader(sock, default_chunk_size) + return co_wrap(function(max_chunk_size) + local remaining = 0 + local length + max_chunk_size = max_chunk_size or default_chunk_size + + repeat + -- If we still have data on this chunk + if max_chunk_size and remaining > 0 then + + if remaining > max_chunk_size then + -- Consume up to max_chunk_size + length = max_chunk_size + remaining = remaining - max_chunk_size + else + -- Consume all remaining + length = remaining + remaining = 0 + end + else -- This is a fresh chunk + + -- Receive the chunk size + local str, err = sock:receive("*l") + if not str then + co_yield(nil, err) + end + + length = tonumber(str, 16) + + if not length then + co_yield(nil, "unable to read chunksize") + end + + if max_chunk_size and length > max_chunk_size then + -- Consume up to max_chunk_size + remaining = length - max_chunk_size + length = max_chunk_size + end + end + + if length > 0 then + local str, err = sock:receive(length) + if not str then + co_yield(nil, err) + end + + max_chunk_size = co_yield(str) or default_chunk_size + + -- If we're finished with this chunk, read the carriage return. + if remaining == 0 then + sock:receive(2) -- read \r\n + end + else + -- Read the last (zero length) chunk's carriage return + sock:receive(2) -- read \r\n + end + + until length == 0 + end) +end + + +local function _body_reader(sock, content_length, default_chunk_size) + return co_wrap(function(max_chunk_size) + max_chunk_size = max_chunk_size or default_chunk_size + + if not content_length and max_chunk_size then + -- We have no length, but wish to stream. + -- HTTP 1.0 with no length will close connection, so read chunks to the end. + repeat + local str, err, partial = sock:receive(max_chunk_size) + if not str and err == "closed" then + co_yield(partial, err) + end + + max_chunk_size = tonumber(co_yield(str) or default_chunk_size) + if max_chunk_size and max_chunk_size < 0 then max_chunk_size = nil end + + if not max_chunk_size then + ngx_log(ngx_ERR, "Buffer size not specified, bailing") + break + end + until not str + + elseif not content_length then + -- We have no length but don't wish to stream. + -- HTTP 1.0 with no length will close connection, so read to the end. + co_yield(sock:receive("*a")) + + elseif not max_chunk_size then + -- We have a length and potentially keep-alive, but want everything. + co_yield(sock:receive(content_length)) + + else + -- We have a length and potentially a keep-alive, and wish to stream + -- the response. + local received = 0 + repeat + local length = max_chunk_size + if received + length > content_length then + length = content_length - received + end + + if length > 0 then + local str, err = sock:receive(length) + if not str then + co_yield(nil, err) + end + received = received + length + + max_chunk_size = tonumber(co_yield(str) or default_chunk_size) + if max_chunk_size and max_chunk_size < 0 then max_chunk_size = nil end + + if not max_chunk_size then + ngx_log(ngx_ERR, "Buffer size not specified, bailing") + break + end + end + + until length == 0 + end + end) +end + + +local function _no_body_reader() + return nil +end + + +local function _read_body(res) + local reader = res.body_reader + + if not reader then + -- Most likely HEAD or 304 etc. + return nil, "no body to be read" + end + + local chunks = {} + local c = 1 + + local chunk, err + repeat + chunk, err = reader() + + if err then + return nil, err, tbl_concat(chunks) -- Return any data so far. + end + if chunk then + chunks[c] = chunk + c = c + 1 + end + until not chunk + + return tbl_concat(chunks) +end + + +local function _trailer_reader(sock) + return co_wrap(function() + co_yield(_receive_headers(sock)) + end) +end + + +local function _read_trailers(res) + local reader = res.trailer_reader + if not reader then + return nil, "no trailers" + end + + local trailers = reader() + setmetatable(res.headers, { __index = trailers }) +end + + +local function _send_body(sock, body) + if type(body) == 'function' then + repeat + local chunk, err, partial = body() + + if chunk then + local ok, err = sock:send(chunk) + + if not ok then + return nil, err + end + elseif err ~= nil then + return nil, err, partial + end + + until chunk == nil + elseif body ~= nil then + local bytes, err = sock:send(body) + + if not bytes then + return nil, err + end + end + return true, nil +end + + +local function _handle_continue(sock, body) + local status, version, reason, err = _receive_status(sock) + if not status then + return nil, nil, err + end + + -- Only send body if we receive a 100 Continue + if status == 100 then + local ok, err = sock:receive("*l") -- Read carriage return + if not ok then + return nil, nil, err + end + _send_body(sock, body) + end + return status, version, err +end + + +function _M.send_request(self, params) + -- Apply defaults + setmetatable(params, { __index = DEFAULT_PARAMS }) + + local sock = self.sock + local body = params.body + local headers = http_headers.new() + + local params_headers = params.headers + if params_headers then + -- We assign one by one so that the metatable can handle case insensitivity + -- for us. You can blame the spec for this inefficiency. + for k,v in pairs(params_headers) do + headers[k] = v + end + end + + -- Ensure minimal headers are set + if type(body) == 'string' and not headers["Content-Length"] then + headers["Content-Length"] = #body + end + if not headers["Host"] then + if (str_sub(self.host, 1, 5) == "unix:") then + return nil, "Unable to generate a useful Host header for a unix domain socket. Please provide one." + end + -- If we have a port (i.e. not connected to a unix domain socket), and this + -- port is non-standard, append it to the Host heaer. + if self.port then + if self.ssl and self.port ~= 443 then + headers["Host"] = self.host .. ":" .. self.port + elseif not self.ssl and self.port ~= 80 then + headers["Host"] = self.host .. ":" .. self.port + else + headers["Host"] = self.host + end + else + headers["Host"] = self.host + end + end + if not headers["User-Agent"] then + headers["User-Agent"] = _M._USER_AGENT + end + if params.version == 1.0 and not headers["Connection"] then + headers["Connection"] = "Keep-Alive" + end + + params.headers = headers + + -- Format and send request + local req = _format_request(params) + ngx_log(ngx_DEBUG, "\n", req) + local bytes, err = sock:send(req) + + if not bytes then + return nil, err + end + + -- Send the request body, unless we expect: continue, in which case + -- we handle this as part of reading the response. + if headers["Expect"] ~= "100-continue" then + local ok, err, partial = _send_body(sock, body) + if not ok then + return nil, err, partial + end + end + + return true +end + + +function _M.read_response(self, params) + local sock = self.sock + + local status, version, reason, err + + -- If we expect: continue, we need to handle this, sending the body if allowed. + -- If we don't get 100 back, then status is the actual status. + if params.headers["Expect"] == "100-continue" then + local _status, _version, _err = _handle_continue(sock, params.body) + if not _status then + return nil, _err + elseif _status ~= 100 then + status, version, err = _status, _version, _err + end + end + + -- Just read the status as normal. + if not status then + status, version, reason, err = _receive_status(sock) + if not status then + return nil, err + end + end + + + local res_headers, err = _receive_headers(sock) + if not res_headers then + return nil, err + end + + -- keepalive is true by default. Determine if this is correct or not. + local ok, connection = pcall(str_lower, res_headers["Connection"]) + if ok then + if (version == 1.1 and connection == "close") or + (version == 1.0 and connection ~= "keep-alive") then + self.keepalive = false + end + else + -- no connection header + if version == 1.0 then + self.keepalive = false + end + end + + local body_reader = _no_body_reader + local trailer_reader, err + local has_body = false + + -- Receive the body_reader + if _should_receive_body(params.method, status) then + local ok, encoding = pcall(str_lower, res_headers["Transfer-Encoding"]) + if ok and version == 1.1 and encoding == "chunked" then + body_reader, err = _chunked_body_reader(sock) + has_body = true + else + + local ok, length = pcall(tonumber, res_headers["Content-Length"]) + if ok then + body_reader, err = _body_reader(sock, length) + has_body = true + end + end + end + + if res_headers["Trailer"] then + trailer_reader, err = _trailer_reader(sock) + end + + if err then + return nil, err + else + return { + status = status, + reason = reason, + headers = res_headers, + has_body = has_body, + body_reader = body_reader, + read_body = _read_body, + trailer_reader = trailer_reader, + read_trailers = _read_trailers, + } + end +end + + +function _M.request(self, params) + params = tbl_copy(params) -- Take by value + local res, err = self:send_request(params) + if not res then + return res, err + else + return self:read_response(params) + end +end + + +function _M.request_pipeline(self, requests) + requests = tbl_copy(requests) -- Take by value + + for _, params in ipairs(requests) do + if params.headers and params.headers["Expect"] == "100-continue" then + return nil, "Cannot pipeline request specifying Expect: 100-continue" + end + + local res, err = self:send_request(params) + if not res then + return res, err + end + end + + local responses = {} + for i, params in ipairs(requests) do + responses[i] = setmetatable({ + params = params, + response_read = false, + }, { + -- Read each actual response lazily, at the point the user tries + -- to access any of the fields. + __index = function(t, k) + local res, err + if t.response_read == false then + res, err = _M.read_response(self, t.params) + t.response_read = true + + if not res then + ngx_log(ngx_ERR, err) + else + for rk, rv in pairs(res) do + t[rk] = rv + end + end + end + return rawget(t, k) + end, + }) + end + return responses +end + +function _M.request_uri(self, uri, params) + params = tbl_copy(params or {}) -- Take by value + + local parsed_uri, err = self:parse_uri(uri, false) + if not parsed_uri then + return nil, err + end + + local scheme, host, port, path, query = unpack(parsed_uri) + if not params.path then params.path = path end + if not params.query then params.query = query end + + -- See if we should use a proxy to make this request + local proxy_uri = self:get_proxy_uri(scheme, host) + + -- Make the connection either through the proxy or directly + -- to the remote host + local c, err + + if proxy_uri then + c, err = self:connect_proxy(proxy_uri, scheme, host, port) + else + c, err = self:connect(host, port) + end + + if not c then + return nil, err + end + + if proxy_uri then + if scheme == "http" then + -- When a proxy is used, the target URI must be in absolute-form + -- (RFC 7230, Section 5.3.2.). That is, it must be an absolute URI + -- to the remote resource with the scheme, host and an optional port + -- in place. + -- + -- Since _format_request() constructs the request line by concatenating + -- params.path and params.query together, we need to modify the path + -- to also include the scheme, host and port so that the final form + -- in conformant to RFC 7230. + if port == 80 then + params.path = scheme .. "://" .. host .. path + else + params.path = scheme .. "://" .. host .. ":" .. port .. path + end + end + + if scheme == "https" then + -- don't keep this connection alive as the next request could target + -- any host and re-using the proxy tunnel for that is not possible + self.keepalive = false + end + + -- self:connect_uri() set the host and port to point to the proxy server. As + -- the connection to the proxy has been established, set the host and port + -- to point to the actual remote endpoint at the other end of the tunnel to + -- ensure the correct Host header added to the requests. + self.host = host + self.port = port + end + + if scheme == "https" then + local verify = true + if params.ssl_verify == false then + verify = false + end + local ok, err = self:ssl_handshake(nil, host, verify) + if not ok then + return nil, err + end + end + + local res, err = self:request(params) + if not res then + return nil, err + end + + local body, err = res:read_body() + if not body then + return nil, err + end + + res.body = body + + local ok, err = self:set_keepalive() + if not ok then + ngx_log(ngx_ERR, err) + end + + return res, nil +end + + +function _M.get_client_body_reader(self, chunksize, sock) + chunksize = chunksize or 65536 + + if not sock then + local ok, err + ok, sock, err = pcall(ngx_req_socket) + + if not ok then + return nil, sock -- pcall err + end + + if not sock then + if err == "no body" then + return nil + else + return nil, err + end + end + end + + local headers = ngx_req_get_headers() + local length = headers.content_length + local encoding = headers.transfer_encoding + if length then + return _body_reader(sock, tonumber(length), chunksize) + elseif encoding and str_lower(encoding) == 'chunked' then + -- Not yet supported by ngx_lua but should just work... + return _chunked_body_reader(sock, chunksize) + else + return nil + end +end + + +function _M.proxy_request(self, chunksize) + return self:request{ + method = ngx_req_get_method(), + path = ngx_re_gsub(ngx_var.uri, "\\s", "%20", "jo") .. ngx_var.is_args .. (ngx_var.query_string or ""), + body = self:get_client_body_reader(chunksize), + headers = ngx_req_get_headers(), + } +end + + +function _M.proxy_response(self, response, chunksize) + if not response then + ngx_log(ngx_ERR, "no response provided") + return + end + + ngx.status = response.status + + -- Filter out hop-by-hop headeres + for k,v in pairs(response.headers) do + if not HOP_BY_HOP_HEADERS[str_lower(k)] then + ngx_header[k] = v + end + end + + local reader = response.body_reader + repeat + local chunk, err = reader(chunksize) + if err then + ngx_log(ngx_ERR, err) + break + end + + if chunk then + local res, err = ngx_print(chunk) + if not res then + ngx_log(ngx_ERR, err) + break + end + end + until not chunk +end + +function _M.set_proxy_options(self, opts) + self.proxy_opts = tbl_copy(opts) -- Take by value +end + +function _M.get_proxy_uri(self, scheme, host) + if not self.proxy_opts then + return nil + end + + -- Check if the no_proxy option matches this host. Implementation adapted + -- from lua-http library (https://github.com/daurnimator/lua-http) + if self.proxy_opts.no_proxy then + if self.proxy_opts.no_proxy == "*" then + -- all hosts are excluded + return nil + end + + local no_proxy_set = {} + -- wget allows domains in no_proxy list to be prefixed by "." + -- e.g. no_proxy=.mit.edu + for host_suffix in ngx_re_gmatch(self.proxy_opts.no_proxy, "\\.?([^,]+)") do + no_proxy_set[host_suffix[1]] = true + end + + -- From curl docs: + -- matched as either a domain which contains the hostname, or the + -- hostname itself. For example local.com would match local.com, + -- local.com:80, and www.local.com, but not www.notlocal.com. + -- + -- Therefore, we keep stripping subdomains from the host, compare + -- them to the ones in the no_proxy list and continue until we find + -- a match or until there's only the TLD left + repeat + if no_proxy_set[host] then + return nil + end + + -- Strip the next level from the domain and check if that one + -- is on the list + host = ngx_re_sub(host, "^[^.]+\\.", "") + until not ngx_re_find(host, "\\.") + end + + if scheme == "http" and self.proxy_opts.http_proxy then + return self.proxy_opts.http_proxy + end + + if scheme == "https" and self.proxy_opts.https_proxy then + return self.proxy_opts.https_proxy + end + + return nil +end + + +function _M.connect_proxy(self, proxy_uri, scheme, host, port) + -- Parse the provided proxy URI + local parsed_proxy_uri, err = self:parse_uri(proxy_uri, false) + if not parsed_proxy_uri then + return nil, err + end + + -- Check that the scheme is http (https is not supported for + -- connections between the client and the proxy) + local proxy_scheme = parsed_proxy_uri[1] + if proxy_scheme ~= "http" then + return nil, "protocol " .. proxy_scheme .. " not supported for proxy connections" + end + + -- Make the connection to the given proxy + local proxy_host, proxy_port = parsed_proxy_uri[2], parsed_proxy_uri[3] + local c, err = self:connect(proxy_host, proxy_port) + if not c then + return nil, err + end + + if scheme == "https" then + -- Make a CONNECT request to create a tunnel to the destination through + -- the proxy. The request-target and the Host header must be in the + -- authority-form of RFC 7230 Section 5.3.3. See also RFC 7231 Section + -- 4.3.6 for more details about the CONNECT request + local destination = host .. ":" .. port + local res, err = self:request({ + method = "CONNECT", + path = destination, + headers = { + ["Host"] = destination + } + }) + + if not res then + return nil, err + end + + if res.status < 200 or res.status > 299 then + return nil, "failed to establish a tunnel through a proxy: " .. res.status + end + end + + return c, nil +end + +return _M diff --git a/website/backend/app/libs/resty/http/http_headers.lua b/website/backend/app/libs/resty/http/http_headers.lua new file mode 100755 index 0000000..56069ec --- /dev/null +++ b/website/backend/app/libs/resty/http/http_headers.lua @@ -0,0 +1,44 @@ +local rawget, rawset, setmetatable = + rawget, rawset, setmetatable + +local str_lower = string.lower + +local _M = { + _VERSION = '0.12', +} + + +-- Returns an empty headers table with internalised case normalisation. +function _M.new() + local mt = { + normalised = {}, + } + + mt.__index = function(t, k) + return rawget(t, mt.normalised[str_lower(k)]) + end + + mt.__newindex = function(t, k, v) + local k_normalised = str_lower(k) + + -- First time seeing this header field? + if not mt.normalised[k_normalised] then + -- Create a lowercased entry in the metatable proxy, with the value + -- of the given field case + mt.normalised[k_normalised] = k + + -- Set the header using the given field case + rawset(t, k, v) + else + -- We're being updated just with a different field case. Use the + -- normalised metatable proxy to give us the original key case, and + -- perorm a rawset() to update the value. + rawset(t, mt.normalised[k_normalised], v) + end + end + + return setmetatable({}, mt) +end + + +return _M diff --git a/website/backend/app/libs/resty/moongoo.lua b/website/backend/app/libs/resty/moongoo.lua new file mode 100755 index 0000000..ad412eb --- /dev/null +++ b/website/backend/app/libs/resty/moongoo.lua @@ -0,0 +1,146 @@ +local cbson = require("cbson") +local connection = require("resty.moongoo.connection") +local database = require("resty.moongoo.database") +local parse_uri = require("resty.moongoo.utils").parse_uri +local auth_scram = require("resty.moongoo.auth.scram") +local auth_cr = require("resty.moongoo.auth.cr") + + +local _M = {} + +_M._VERSION = '0.1' +_M.NAME = 'Moongoo' + + +local mt = { __index = _M } + +function _M.new(uri) + local conninfo = parse_uri(uri) + + if not conninfo.scheme or conninfo.scheme ~= "mongodb" then + return nil, "Wrong scheme in connection uri" + end + + local auth_algo = conninfo.query and conninfo.query.authMechanism or "SCRAM-SHA-1" + local w = conninfo.query and conninfo.query.w or 1 + local wtimeout = conninfo.query and conninfo.query.wtimeoutMS or 1000 + local journal = conninfo.query and conninfo.query.journal or false + local ssl = conninfo.query and conninfo.query.ssl or false + + local stimeout = conninfo.query.socketTimeoutMS and conninfo.query.socketTimeoutMS or nil + + return setmetatable({ + connection = nil; + w = w; + wtimeout = wtimeout; + journal = journal; + stimeout = stimeout; + hosts = conninfo.hosts; + default_db = conninfo.database; + user = conninfo.user or nil; + password = conninfo.password or ""; + auth_algo = auth_algo, + ssl = ssl, + version = nil + }, mt) +end + +function _M._auth(self, protocol) + if not self.user then return 1 end + + if not protocol or protocol < cbson.int(3) or self.auth_algo == "MONGODB-CR" then + return auth_cr(self:db(self.default_db), self.user, self.password) + else + return auth_scram(self:db(self.default_db), self.user, self.password) + end + +end + +function _M.connect(self) + if self.connection then return self end + + -- foreach host + for k, v in ipairs(self.hosts) do + -- connect + self.connection, err = connection.new(v.host, v.port, self.stimeout) + if not self.connection then + return nil, err + end + local status, err = self.connection:connect() + if status then + if self.ssl then + self.connection:handshake() + end + if not self.version then + query = self:db(self.default_db):_cmd({ buildInfo = 1 }) + if query then + self.version = query.version + end + end + + local ismaster = self:db("admin"):_cmd("ismaster") + if ismaster and ismaster.ismaster then + -- auth + local r, err = self:_auth(ismaster.maxWireVersion) + if not r then + return nil, err + end + return self + else + -- try to connect to master + if ismaster.primary then + local mhost, mport + string.gsub(ismaster.primary, "([^:]+):([^:]+)", function(host,port) mhost=host; mport=port end) + self.connection:close() + self.connection = nil + self.connection, err = connection.new(mhost, mport, self.stimeout) + if not self.connection then + return nil, err + end + local status, err = self.connection:connect() + if not status then + return nil, err + end + if self.ssl then + self.connection:handshake() + end + if not self.version then + query = self:db(self.default_db):_cmd({ buildInfo = 1 }) + if query then + self.version = query.version + end + end + local ismaster = self:db("admin"):_cmd("ismaster") + if ismaster and ismaster.ismaster then + -- auth + local r, err = self:_auth(ismaster.maxWireVersion) + if not r then + return nil, err + end + return self + else + return nil, "Can't connect to master server" + end + end + end + end + end + return nil, "Can't connect to any of servers" +end + +function _M.close(self) + if self.connection then + self.connection:close() + self.connection = nil + end +end + +function _M.get_reused_times(self) + return self.connection:get_reused_times() +end + +function _M.db(self, dbname) + return database.new(dbname, self) +end + +return _M diff --git a/website/backend/app/libs/resty/moongoo/auth/cr.lua b/website/backend/app/libs/resty/moongoo/auth/cr.lua new file mode 100755 index 0000000..8907789 --- /dev/null +++ b/website/backend/app/libs/resty/moongoo/auth/cr.lua @@ -0,0 +1,32 @@ +local pass_digest = require("resty.moongoo.utils").pass_digest + +local b64 = ngx and ngx.encode_base64 or require("mime").b64 +local unb64 = ngx and ngx.decode_base64 or require("mime").unb64 + +local md5 = ngx and ngx.md5 or function(str) return require("crypto").digest("md5", str) end + +local cbson = require("cbson") + + +local function auth(db, username, password) + local r, err = db:_cmd("getnonce", {}) + if not r then + return nil, err + end + + local digest = md5( r.nonce .. username .. pass_digest ( username , password ) ) + + r, err = db:_cmd("authenticate", { + user = username ; + nonce = r.nonce ; + key = digest ; + }) + + if not r then + return nil, err + end + + return 1 +end + +return auth \ No newline at end of file diff --git a/website/backend/app/libs/resty/moongoo/auth/scram.lua b/website/backend/app/libs/resty/moongoo/auth/scram.lua new file mode 100755 index 0000000..d8d1037 --- /dev/null +++ b/website/backend/app/libs/resty/moongoo/auth/scram.lua @@ -0,0 +1,103 @@ +local Hi = require("resty.moongoo.utils").pbkdf2_hmac_sha1 +local saslprep = require("resty.moongoo.utils").saslprep +local pass_digest = require("resty.moongoo.utils").pass_digest +local xor_bytestr = require("resty.moongoo.utils").xor_bytestr + +local b64 = ngx and ngx.encode_base64 or require("mime").b64 +local unb64 = ngx and ngx.decode_base64 or require("mime").unb64 + +local hmac_sha1 = ngx and ngx.hmac_sha1 or function(str, key) return require("crypto").hmac.digest("sha1", key, str, true) end +local sha1_bin = ngx and ngx.sha1_bin or function(str) return require("crypto").digest("sha1", str, true) end + +local cbson = require("cbson") + + +local function auth(db, username, password) + local username = saslprep(username) + local c_nonce = b64(string.sub(tostring(math.random()), 3 , 14)) + + local first_bare = "n=" .. username .. ",r=" .. c_nonce + + local sasl_start_payload = b64("n,," .. first_bare) + + r, err = db:_cmd("saslStart", { + mechanism = "SCRAM-SHA-1" ; + autoAuthorize = 1 ; + payload = cbson.binary(sasl_start_payload); + }) + + if not r then + return nil, err + end + + + local conversationId = r['conversationId'] + local server_first = r['payload']:raw() + + local parsed_t = {} + for k, v in string.gmatch(server_first, "(%w+)=([^,]*)") do + parsed_t[k] = v + end + + local iterations = tonumber(parsed_t['i']) + local salt = parsed_t['s'] + local s_nonce = parsed_t['r'] + + if not string.sub(s_nonce, 1, 12) == c_nonce then + return nil, 'Server returned an invalid nonce.' + end + + local without_proof = "c=biws,r=" .. s_nonce + + local pbkdf2_key = pass_digest ( username , password ) + local salted_pass = Hi(pbkdf2_key, iterations, unb64(salt), 20) + + local client_key = hmac_sha1(salted_pass, "Client Key") + local stored_key = sha1_bin(client_key) + local auth_msg = first_bare .. ',' .. server_first .. ',' .. without_proof + local client_sig = hmac_sha1(stored_key, auth_msg) + local client_key_xor_sig = xor_bytestr(client_key, client_sig) + local client_proof = "p=" .. b64(client_key_xor_sig) + local client_final = b64(without_proof .. ',' .. client_proof) + local server_key = hmac_sha1(salted_pass, "Server Key") + local server_sig = b64(hmac_sha1(server_key, auth_msg)) + + r, err = db:_cmd("saslContinue",{ + conversationId = conversationId ; + payload = cbson.binary(client_final); + }) + + if not r then + return nil, err + end + + local parsed_s = r['payload']:raw() + parsed_t = {} + for k, v in string.gmatch(parsed_s, "(%w+)=([^,]*)") do + parsed_t[k] = v + end + if parsed_t['v'] ~= server_sig then + return nil, "Server returned an invalid signature." + end + + if not r['done'] then + r, err = db:_cmd("saslContinue", { + conversationId = conversationId ; + payload = cbson.binary("") ; + }) + + if not r then + return nil, err + end + + if not r['done'] then + return nil, 'SASL conversation failed to complete.' + end + + return 1 + end + + return 1 +end + +return auth diff --git a/website/backend/app/libs/resty/moongoo/collection.lua b/website/backend/app/libs/resty/moongoo/collection.lua new file mode 100755 index 0000000..ee5ed18 --- /dev/null +++ b/website/backend/app/libs/resty/moongoo/collection.lua @@ -0,0 +1,377 @@ +local cbson = require("cbson") +local generate_oid = require("resty.moongoo.utils").generate_oid +local cursor = require("resty.moongoo.cursor") + +local _M = {} + +local mt = { __index = _M } + +function _M.new(name, db) + return setmetatable({_db = db, name = name}, mt) +end + +function _M._build_write_concern(self) + return { + j = self._db._moongoo.journal; + w = tonumber(self._db._moongoo.w) and cbson.int(self._db._moongoo.w) or self._db._moongoo.w; + wtimeout = cbson.int(self._db._moongoo.wtimeout); + } +end + +local function check_write_concern(doc, ...) + -- even if write concern failed we may still have successful operation + -- so we check for number of affected docs, and only warn if its > 0 + -- otherwise, we just return nil and error + + if doc.writeConcernError then + if not doc.n then + return nil, doc.writeConcernError.errmsg + else + print(doc.writeConcernError.errmsg) + end + end + return ... +end + +function _M._get_last_error(self) + local write_concern = self:_build_write_concern() + local cmd = { getLastError = cbson.int(1), j = write_concern.j, w = write_concern.w, wtimeout = write_concern.wtimeout } + + local doc, err = self._db:cmd(cmd) + if not doc then + return nil, err + end + + return doc +end + +function _M._check_last_error(self, ...) + local cmd, err = self:_get_last_error() + + if not cmd then + return nil, err + end + + if tostring(cmd.err) == "null" then + return ... + end + + return nil, tostring(cmd.err) +end + +local function ensure_oids(docs) + local docs = docs + local ids = {} + for k,v in ipairs(docs) do + if not docs[k]._id then + docs[k]._id = cbson.oid(generate_oid()) + end + table.insert(ids, docs[k]._id) + end + return docs, ids +end + +local function build_index_names(docs) + local docs = docs + for k,v in ipairs(docs) do + if not v.name then + local name = {} + for n, d in pairs(v.key) do + table.insert(name, n) + end + name = table.concat(name, '_') + docs[k].name = name + end + end + return docs +end + +function _M.insert(self, docs) + -- ensure we have oids + if #docs == 0 then + local newdocs = {} + newdocs[1] = docs + docs = newdocs + end + local docs, ids = ensure_oids(docs) + + self._db._moongoo:connect() + + local server_version = tonumber(string.sub(string.gsub(self._db._moongoo.version, "(%D)", ""), 1, 3)) + + if server_version < 254 then + self._db:insert(self:full_name(), docs) + return self:_check_last_error(ids) + else + local doc, err = self._db:cmd( + { insert = self.name }, + { + documents = docs, + ordered = true, + writeConcern = self:_build_write_concern() + } + ) + + if not doc then + return nil, err + end + + return check_write_concern(doc, ids, doc.n) + end +end + +function _M.create(self, params) + local params = params or {} + local doc, err = self._db:cmd( + { create = self.name }, + params + ) + if not doc then + return nil, err + end + return true +end + +function _M.drop(self) + local doc, err = self._db:cmd( + { drop = self.name }, + {} + ) + if not doc then + return nil, err + end + return true +end + +function _M.drop_index(self, name) + local doc, err = self._db:cmd( + { dropIndexes = self.name }, + { index = name } + ) + if not doc then + return nil, err + end + return true +end + +function _M.ensure_index(self, docs) + docs = build_index_names(docs) + + local doc, err = self._db:cmd( + { createIndexes = self.name }, + { indexes = docs } + ) + if not doc then + return nil, err + end + return true +end + +function _M.full_name(self) + return self._db.name .. "." .. self.name +end + +function _M.options(self) + local doc, err = self._db:cmd( + "listCollections", + { + filter = { name = self.name } + } + ) + if not doc then + return nil, err + end + return doc.cursor.firstBatch[1] +end + +function _M.remove(self, query, single) + local query = query or {} + + if getmetatable(cbson.oid("000000000000000000000000")) == getmetatable(query) then + query = { _id = query } + end + + local doc, err = self._db:cmd( + { delete = self.name }, + { + deletes = {{q=query, limit = single and 1 or 0}}, + ordered = true, + writeConcern = self:_build_write_concern() + } + ) + if not doc then + return nil, err + end + + return check_write_concern(doc, doc.n) +end + +function _M.stats(self) + local doc, err = self._db:cmd( + {collstats = self.name}, + {} + ) + if not doc then + return nil, err + end + return doc +end + +function _M.index_information(self) + local doc, err = self._db:cmd( + { listIndexes = self.name }, + { } + ) + if not doc then + return nil, err + end + return doc.cursor.firstBatch +end + +function _M.rename(self, to_name, drop) + local drop = drop or false + -- rename + local doc, err = self._db._moongoo:db("admin"):cmd( + { renameCollection = self:full_name() }, + { + to = to_name, + dropTarget = drop + } + ) + if not doc then + return nil, err + end + + return self.new(to_name, self._db) +end + +function _M.update(self, query, update, flags) + local flags = flags or {} + local query = query or {} + + if getmetatable(cbson.oid("000000000000000000000000")) == getmetatable(query) then + query = { _id = query } + end + + local update = { + q = query, + u = update, + upsert = flags.upsert or false, + multi = flags.multi or false + } + + local doc, err = self._db:cmd( + { update = self.name }, + { + updates = { update }, + ordered = true, + writeConcern = self:_build_write_concern() + } + ) + if not doc then + return nil, err + end + + return doc.nModified +end + +function _M.save(self, doc) + if not doc._id then + doc._id = cbson.oid(generate_oid()) + end + local r, err = self:update(doc._id, doc, {upsert = true}); + if not r then + return nil, err + end + + return doc._id +end + +function _M.map_reduce(self, map, reduce, flags) + local flags = flags or {} + flags.map = cbson.code(map) + flags.reduce = cbson.code(reduce) + flags.out = flags.out or { inline = true } + + local doc, err = self._db:cmd( + { mapReduce = self.name }, + flags + ) + if not doc then + return nil, err + end + + if doc.results then + return doc.results + end + + return self.new(doc.result, self._db) +end + +function _M.find(self, query, fields) + local query = query or {} + if getmetatable(cbson.oid("000000000000000000000000")) == getmetatable(query) then + query = { _id = query } + end + return cursor.new(self, query, fields) +end + +function _M.find_one(self, query, fields) + local query = query or {} + if getmetatable(cbson.oid("000000000000000000000000")) == getmetatable(query) then + query = { _id = query } + end + + return self:find(query, fields):limit(-1):next() +end + +function _M.find_and_modify(self, query, opts) + local query = query or {} + if getmetatable(cbson.oid("000000000000000000000000")) == getmetatable(query) then + query = { _id = query } + end + + local opts = opts or {} + opts.query = query + + local doc, err = self._db:cmd( + { findAndModify = self.name }, + opts + ) + if not doc then + return nil, err + end + return doc.value +end + +function _M.aggregate(self, pipeline, opts) + local opts = opts or {} + opts.pipeline = pipeline + if not opts.explain then + opts.cursor = {} + end + + local doc, err = self._db:cmd( + { aggregate = self.name }, + opts + ) + if not doc then + return nil, err + end + + if opts.explain then + return doc + end + + -- collection + if opts.pipeline[#opts.pipeline]['$out'] then + return self.new(opts.pipeline[#opts.pipeline]['$out'], self._db) + end + + -- cursor + return cursor.new(self, {}, {}, false, doc.cursor.id):add_batch(doc.cursor.firstBatch) +end + + + +return _M \ No newline at end of file diff --git a/website/backend/app/libs/resty/moongoo/connection.lua b/website/backend/app/libs/resty/moongoo/connection.lua new file mode 100755 index 0000000..fac53f9 --- /dev/null +++ b/website/backend/app/libs/resty/moongoo/connection.lua @@ -0,0 +1,210 @@ +local socket = ngx and ngx.socket.tcp or require("socket").tcp +local cbson = require("cbson") + +local opcodes = { + OP_REPLY = 1; + OP_MSG = 1000; + OP_UPDATE = 2001; + OP_INSERT = 2002; + RESERVED = 2003; + OP_QUERY = 2004; + OP_GET_MORE = 2005; + OP_DELETE = 2006; + OP_KILL_CURSORS = 2007; +} + +local _M = {} + +local mt = { __index = _M } + +function _M.new(host, port, timeout) + local sock = socket() + if timeout then + sock:settimeout(timeout) + end + + return setmetatable({ + sock = sock; + host = host; + port = port; + _id = 0; + }, mt) +end + +function _M.connect(self, host, port) + self.host = host or self.host + self.port = port or self.port + return self.sock:connect(self.host, self.port) +end + +function _M.handshake(self) + if ngx then + self.sock:sslhandshake() + else + local ssl = require("ssl") + self.sock = ssl.wrap(self.sock, {mode = "client", protocol = "tlsv1_2"}) + assert(self.sock) + self.sock:dohandshake() + end +end + +function _M.close(self) + if ngx then + self.sock:setkeepalive() + else + self.sock:close() + end +end + +function _M.get_reused_times(self) + if not self.sock then + return nil, "not initialized" + end + + return self.sock:getreusedtimes() +end + +function _M.settimeout(self, ms) + self.sock:settimeout(ms) +end + +function _M.send(self, data) + return self.sock:send(data) +end + +function _M.receive(self, pat) + return self.sock:receive(pat) +end + +function _M._handle_reply(self) + local header = assert ( self.sock:receive ( 16 ) ) + + local length = cbson.raw_to_uint( string.sub(header , 1 , 4 )) + local r_id = cbson.raw_to_uint( string.sub(header , 5 , 8 )) + local r_to = cbson.raw_to_uint( string.sub(header , 9 , 12 )) + local opcode = cbson.raw_to_uint( string.sub(header , 13 , 16 )) + + assert ( opcode == cbson.uint(opcodes.OP_REPLY ) ) + assert ( r_to == cbson.uint(self._id) ) + + local data = assert ( self.sock:receive ( tostring(length-16 ) ) ) + + local flags = cbson.raw_to_uint( string.sub(data , 1 , 4 )) + local cursor_id = cbson.raw_to_uint( string.sub(data , 5 , 12 )) + local from = cbson.raw_to_uint( string.sub(data , 13 , 16 )) + local number = tonumber(tostring(cbson.raw_to_uint( string.sub(data , 17 , 20 )))) + + local docs = string.sub(data , 21) + + local pos = 1 + local index = 0 + local r_docs = {} + while index < number do + local bson_size = tonumber(tostring(cbson.raw_to_uint(docs:sub(pos, pos+3)))) + + local dt = docs:sub(pos,pos+bson_size-1) -- get bson data according to size + + table.insert(r_docs, cbson.decode(dt)) + + pos = pos + bson_size + index = index + 1 + end + + return flags, cursor_id, from, number, r_docs +end + +function _M._build_header(self, op, payload_size) + local size = cbson.uint_to_raw(cbson.uint(payload_size+16), 4) + local op = cbson.uint_to_raw(cbson.uint(op), 4) + self._id = self._id+1 + local id = cbson.uint_to_raw(cbson.uint(self._id), 4) + local reply_to = "\0\0\0\0" + return size .. id .. reply_to .. op +end + +function _M._query(self, collection, query, to_skip, to_return, selector, flags) + local flags = { + tailable = flags and flags.tailable and 1 or 0, + slaveok = flags and flags.slaveok and 1 or 0, + notimeout = flags and flags.notimeout and 1 or 0, + await = flags and flags.await and 1 or 0, + exhaust = flags and flags.exhaust and 1 or 0, + partial = flags and flags.partial and 1 or 0 + } + + local flagset = cbson.int_to_raw( + cbson.int( + 2 * flags["tailable"] + + 2^2 * flags["slaveok"] + + 2^4 * flags["notimeout"] + + 2^5 * flags["await"] + + 2^6 * flags["exhaust"] + + 2^7 * flags["partial"] + ), + 4) + + local selector = selector and #selector and cbson.encode(selector) or "" + + local to_skip = cbson.int_to_raw(cbson.int(to_skip), 4) + local to_return = cbson.int_to_raw(cbson.int(to_return), 4) + + local size = 4 + #collection + 1 + 4 + 4 + #query + #selector + + local header = self:_build_header(opcodes["OP_QUERY"], size) + + local data = header .. flagset .. collection .. "\0" .. to_skip .. to_return .. query .. selector + + assert(self:send(data)) + return self:_handle_reply() +end + +function _M._insert(self, collection, docs, flags) + local encoded_docs = {} + for k, doc in ipairs(docs) do + encoded_docs[k] = cbson.encode(doc) + end + string_docs = table.concat(encoded_docs) + + local flags = { + continue_on_error = flags and flags.continue_on_error and 1 or 0 + } + + local flagset = cbson.int_to_raw( + cbson.int( + 2 * flags["continue_on_error"] + ), + 4) + + local size = 4 + 1 + #collection + #string_docs + local header = self:_build_header(opcodes["OP_INSERT"], size) + + local data = header .. flagset .. collection .. "\0" .. string_docs + + assert(self:send(data)) + + return true -- Mongo doesn't send a reply +end + +function _M._kill_cursors(self, id) + local id = cbson.uint_to_raw(id, 8) + local num = cbson.int_to_raw(cbson.int(1), 4) + local zero = cbson.int_to_raw(cbson.int(0), 4) + local size = 8+4+4 + local header = self:_build_header(opcodes["OP_KILL_CURSORS"], size) + local data = header .. zero .. num .. id + assert(self:send(data)) + return true -- Mongo doesn't send a reply +end + +function _M._get_more(self, collection, number, cursor) + local num = cbson.int_to_raw(cbson.int(number), 4) + local zero = cbson.int_to_raw(cbson.int(0), 4) + local cursor = cbson.uint_to_raw(cursor, 8) + local size = 4+#collection+1+4+8 + local header = self:_build_header(opcodes["OP_GET_MORE"], size) + local data = header .. zero .. collection .. '\0' .. num .. cursor + assert(self:send(data)) + return self:_handle_reply() +end + +return _M \ No newline at end of file diff --git a/website/backend/app/libs/resty/moongoo/cursor.lua b/website/backend/app/libs/resty/moongoo/cursor.lua new file mode 100755 index 0000000..13bc8f5 --- /dev/null +++ b/website/backend/app/libs/resty/moongoo/cursor.lua @@ -0,0 +1,256 @@ +local cbson = require("cbson") +local bit = require("bit") + + +local function check_bit(num, bitnum) + return bit.band(num,math.pow(2,bitnum)) ~= 0 -- and true or false +end + +local _M = {} + +local mt = { __index = _M } + +function _M.new(collection, query, fields, explain, id) + return setmetatable( + { + _collection = collection, + _query = query, + _fields = fields, + _id = id or cbson.uint(0), + _skip = 0, + _limit = 0, + _docs = {}, + _started = false, + _cnt = 0, + _comment = nil, + _hint = nil, + _max_scan = nil , + _max_time_ms = nil, + _read_preference = nil, + _snapshot = nil, + _sort = nil, + _await = false, + _tailable = false, + _explain = explain or false + }, + mt) +end + +function _M.tailable(self, tailable) + self._tailable = tailable + return self +end + +function _M.await(self, await) + self._await = await + return self +end + + +function _M.comment(self, comment) + self._comment = comment + return self +end + +function _M.hint(self, hint) + self._hint = hint + return self +end + +function _M.max_scan(self, max_scan) + self._max_scan = max_scan + return self +end + +function _M.max_time_ms(self, max_time_ms) + self._max_time_ms = max_time_ms + return self +end + +function _M.read_preference(self, read_preference) + self._read_preference = read_preference + return self +end + +function _M.snapshot(self, snapshot) + self._snapshot = snapshot + return self +end + +function _M.sort(self, sort) + self._sort = sort + return self +end + + +function _M.clone(self, explain) + local clone = self.new(self._collection, self._query, self._fields, explain) + clone:limit(self._limit) + clone:skip(self._skip) + + clone:comment(self._comment) + clone:hint(self._hint) + clone:max_scan(self._max_scan) + clone:max_time_ms(self._max_time_ms) + clone:read_preference(self._read_preference) + clone:snapshot(self._snapshot) + clone:sort(self._sort) + + return clone +end + +function _M.skip(self, skip) + if self._started then + print("Can's set skip after starting cursor") + else + self._skip = skip + end + return self +end + +function _M.limit(self, limit) + if self._started then + print("Can's set limit after starting cursor") + else + self._limit = limit + end + return self +end + +function _M._build_query(self) + local ext = {} + if self._comment then ext['$comment'] = self._comment end + if self._explain then ext['$explain'] = true end + + + if self._hint then ext['$hint'] = self._hint end + if self._max_scan then ext['$maxScan'] = self._max_scan end + if self._max_time_ms then ext['$maxTimeMS'] = self._max_time_ms end + if self._read_preference then ext['$readPreference'] = self._read_preference end + if self._snapshot then ext['$snapshot'] = true end + if self._sort then ext['$orderby'] = self._sort end + + ext['$query'] = self._query + + return cbson.encode(ext) +end + +function _M.next(self) + local moongoo, err = self._collection._db._moongoo:connect() + if not moongoo then + return nil, err + end + + if self:_finished() then + if self._id ~= cbson.uint(0) then + self._collection._db._moongoo.connection:_kill_cursors(self._id) + self._id = cbson.uint(0) + end + return nil, "no more data" + end + + if (not self._started) and (self._id == cbson.uint(0)) then + + -- query and add id and batch + local flags, id, from, number, docs = self._collection._db._moongoo.connection:_query(self._collection:full_name(), self:_build_query(), self._skip, self._limit, self._fields, {tailable = self._tailable, await = self._await}) + + flags = tonumber(tostring(flags)) -- bitop can't work with cbson.int, so... + + if check_bit(flags, 1) then -- QueryFailure + return nil, docs[1]['$err'] -- why is this $err and not errmsg, like others?? + end + self._id = id + self:add_batch(docs) + elseif #self._docs == 0 and self._id ~= cbson.uint(0) then + -- we have something to fetch - get_more and add_batch + local flags, id, from, number, docs = self._collection._db._moongoo.connection:_get_more(self._collection:full_name(), self._limit, self._id) + + flags = tonumber(tostring(flags)) -- bitop can't work with cbson.int, so... + + if check_bit(flags, 0) then -- QueryFailure + return nil, "wrong cursor id" + end + self:add_batch(docs) + self._id = id + + elseif #self._docs == 0 then--or self._id == cbson.uint(0) then + return nil, "no more data" + end + self._cnt = self._cnt+1 + return table.remove(self._docs, 1) or nil, 'No more data' +end + +function _M.all(self) + local docs = {} + while true do + local doc = self:next() + if doc == nil then break end + table.insert(docs, doc) + end + return docs +end + +function _M.rewind(self) + self._started = false + self._docs = {} + self._collection._db._moongoo.connection:_kill_cursors(self._id) + self._id = cbson.uint(0) + return self +end + +function _M.count(self) + local doc, err = self._collection._db:cmd( + { count = self._collection.name }, + { + query = self._query, + skip = self._skip, + limit = self._limit + } + ) + if not doc then + return nil, err + end + + return doc and doc.n or 0 +end + +function _M.distinct(self, key) + local doc, err = self._collection._db:cmd( + { distinct = self._collection.name }, + { + query = self._query, + key = key + } + ) + if not doc then + return nil, err + end + + return doc and doc.values or {} +end + +function _M.explain(self) + return self:clone(true):sort(nil):next() +end + +function _M.add_batch(self, docs) + self._started = true + for k,v in ipairs(docs) do + table.insert(self._docs, v) + end + return self +end + +function _M._finished(self) + if self._limit == 0 then + return false + else + if self._cnt >= math.abs(self._limit) then + return true + else + return false + end + end +end + +return _M diff --git a/website/backend/app/libs/resty/moongoo/database.lua b/website/backend/app/libs/resty/moongoo/database.lua new file mode 100755 index 0000000..4b056e8 --- /dev/null +++ b/website/backend/app/libs/resty/moongoo/database.lua @@ -0,0 +1,70 @@ +local cbson = require("cbson") +local collection = require("resty.moongoo.collection") +local gridfs = require("resty.moongoo.gridfs") + +local _M = {} + +local mt = { __index = _M } + +function _M.new(name, moongoo) + return setmetatable({name = name, _moongoo = moongoo}, mt) +end + +function _M.collection(self, name) + return collection.new(name, self) +end + +function _M.gridfs(self, name) + return gridfs.new(self,name) +end + +function _M.cmd(self, cmd, params) + local r, err = self._moongoo:connect() + if not r then + return nil, err + end + return self:_cmd(cmd, params) +end + +function _M._cmd(self, cmd, params) + local params = params or {} + if type(cmd) == "table" then + local tmpcmd = '' + for k,v in pairs(cmd) do + params[k] = v + tmpcmd = k + end + cmd = tmpcmd + else + params[cmd] = true + end + local cmd = cbson.encode_first(cmd, params) + + local _,_,_,_,docs = self._moongoo.connection:_query(self.name..".$cmd", cmd, 0, 1) + + if not docs[1] then + return nil, "Empty reply from mongodb" + end + + if not docs[1].ok or docs[1].ok == 0 then + return nil, docs[1].errmsg + end + + return docs[1] +end + +function _M.insert(self, collection, docs) + local r, err = self._moongoo:connect() + if not r then + return nil, err + end + return self:_insert(collection, docs) +end + +function _M._insert(self, collection, docs) + self._moongoo.connection:_insert(collection, docs) + return +end + + +return _M \ No newline at end of file diff --git a/website/backend/app/libs/resty/moongoo/gridfs.lua b/website/backend/app/libs/resty/moongoo/gridfs.lua new file mode 100755 index 0000000..34008b0 --- /dev/null +++ b/website/backend/app/libs/resty/moongoo/gridfs.lua @@ -0,0 +1,57 @@ +local cbson = require("cbson") +local gfsfile = require("resty.moongoo.gridfs.file") + +local _M = {} + +local mt = { __index = _M } + +function _M.new(db, name) + local name = name or 'fs' + return setmetatable( + { + _db = db, + _name = name, + _files = db:collection(name .. '.files'), + _chunks = db:collection(name .. '.chunks') + }, + mt) +end + +function _M.list(self) + return self._files:find({}):distinct('filename') +end + +function _M.remove(self, id) + local r,err = self._files:remove({_id = cbson.oid(id)}) + if not r then + return nil, "Failed to remove file metadata: "..err + end + r,err = self._chunks:remove({files_id = cbson.oid(id)}); + if not r then + return nil, "Failed to remove file chunks: "..err + end + return r +end + +function _M.find_version(self, name, version) + -- Positive numbers are absolute and negative ones relative + local cursor = self._files:find({filename = name}, {_id = 1}):limit(-1) + cursor:sort({uploadDate = (version < 0) and cbson.int(-1) or cbson.int(1)}):skip(version < 0 and (math.abs(version) - 1) or version) + local doc, err = cursor:next() + if not doc then + return nil, "No such file/version" + end + return doc._id +end + +function _M.open(self, id) + return gfsfile.open(self, id) +end + + +function _M.create(self, name, opts, safe) + return gfsfile.new(self, name, opts, safe) +end + +return _M + diff --git a/website/backend/app/libs/resty/moongoo/gridfs/file.lua b/website/backend/app/libs/resty/moongoo/gridfs/file.lua new file mode 100755 index 0000000..033d5ee --- /dev/null +++ b/website/backend/app/libs/resty/moongoo/gridfs/file.lua @@ -0,0 +1,219 @@ +local cbson = require("cbson") +local generate_oid = require("resty.moongoo.utils").generate_oid + + +local _M = {} + +local mt = { __index = _M } + +function _M.new(gridfs, name, opts, safe, read_only) + local read_only = read_only or false + local safe = safe == nil and true or safe + + opts = opts or {} + opts.filename = name + opts.length = opts.length or cbson.uint(0) + opts.chunkSize = opts.chunkSize or cbson.uint(261120) + + local write_only = true + if not safe then + write_only = false + end + + return setmetatable( + { + _gridfs = gridfs, + _meta = opts, + _write_only = write_only, + _read_only = read_only, + _pos = 0, + _chunks = {}, + _closed = false, + _buffer = '', + _n = 0 + }, + mt) +end + +function _M.open(gridfs, id) + -- try to fetch + local doc, err = gridfs._files:find_one({ _id = id}) + if not doc then + return nil, "No such file" + else + return _M.new(gridfs, doc.filename, doc, false, true) + end +end + +-- props + +function _M.content_type(self) return self._meta.contentType end +function _M.filename(self) return self._meta.filename end +function _M.md5(self) return self._meta.md5 end +function _M.metadata(self) return self._meta.metadata end +function _M.raw_length(self) return self._meta.length end +function _M.raw_chunk_size(self) return self._meta.chunkSize end +function _M.date(self) return self._meta.uploadDate end + +function _M.length(self) return tonumber(tostring(self._meta.length)) end +function _M.chunk_size(self) return tonumber(tostring(self._meta.chunkSize)) end + +-- reading + +function _M.read(self) + if self._write_only then + return nil, "Can't read from write-only file" + end + + if self._pos >= (self:length() or 0) then + return nil, "EOF" + end + + local n = math.modf(self._pos / self:chunk_size()) + local query = {files_id = self._meta._id, n = n} + local fields = {_id = false, data = true} + + local chunk = self._gridfs._chunks:find_one(query, fields) + if not chunk then + return nil, "EOF?" + end + + return self:_slice(n, chunk.data) +end + +function _M.seek(self, pos) + self._pos = pos + return self +end + +function _M.tell(self) + return self._pos +end + +function _M.slurp(self) + local data = {} + local pos = self._pos + self:seek(0) + while true do + local chunk = self:read() + if not chunk then break end + table.insert(data, chunk) + end + self:seek(pos) + return table.concat(data) +end + +-- writing + +function _M.write(self, data) + if self._read_only then + return nil, "Can't write to read-only file" + end + + if self._closed then + return nil, "Can't write to closed file" + end + + self._buffer = self._buffer .. data + self._meta.length = self._meta.length + data:len() + + while self._buffer:len() >= self:chunk_size() do + local r, res = self:_chunk() + if not r then + return nil, err + end + end +end + +function _M.close(self) + + if self._closed then + return nil, "File already closed" + end + self._closed = true + + self:_chunk() -- enqueue/write last chunk of data + + if self._write_only then + -- insert all collected chunks + for k, v in ipairs(self._chunks) do + local r, err = self._gridfs._chunks:insert(v) + if not r then + return nil, err + end + end + end + + -- ensure indexes + self._gridfs._files:ensure_index({{ key = {filename = true}}}) + self._gridfs._chunks:ensure_index({ { key = {files_id = 1, n = 1}, unique = true } }); + -- compute md5 + local res, err = self._gridfs._db:cmd({filemd5 = self:_files_id()}, {root = self._gridfs._name}) + if not res then + return nil, err + end + local file_md5 = res.md5 + -- insert metadata + local ids, n = self._gridfs._files:insert(self:_metadata(file_md5)) + + if not ids then + return nil, n + end + -- return metadata + return ids[1] +end + +-- private + +function _M._files_id(self) + if not self._meta._id then + self._meta._id = cbson.oid(generate_oid()) + end + return self._meta._id +end + +function _M._metadata(self, file_md5) + local doc = { + _id = self:_files_id(), + length = self:raw_length(), + chunkSize = self:raw_chunk_size(), + uploadDate = cbson.date(os.time(os.date('!*t'))*1000), + md5 = file_md5, + filename = self:filename() or nil, + content_type = self:content_type() or nil, + metadata = self:metadata() or nil + } + + return doc +end + +function _M._slice(self, n, chunk) + local offset = self._pos - (n * self:chunk_size()) + local chunk = chunk:raw() + self._pos = self._pos + chunk:len() + return chunk:sub(offset+1); +end + +function _M._chunk(self) + local chunk = self._buffer:sub(1,self:chunk_size()) + if not chunk then + return + end + self._buffer = self._buffer:sub(self:chunk_size()+1) + local n = self._n + self._n = self._n+1 + local data = cbson.binary("") + data:raw(chunk, chunk:len()) + if self._write_only then + -- collect chunks for insert + table.insert(self._chunks, {files_id = self:_files_id(), n = cbson.uint(n), data = data}) + return true + else + -- insert immidiately, so we can read back (ugh) + return self._gridfs._chunks:insert({{files_id = self:_files_id(), n = cbson.uint(n), data = data}}) + end +end + + + +return _M diff --git a/website/backend/app/libs/resty/moongoo/utils.lua b/website/backend/app/libs/resty/moongoo/utils.lua new file mode 100755 index 0000000..e0dce3f --- /dev/null +++ b/website/backend/app/libs/resty/moongoo/utils.lua @@ -0,0 +1,192 @@ +local bit = require("bit") +local cbson = require("cbson") + +local md5 = ngx and ngx.md5 or function(str) return require("crypto").digest("md5", str) end +local hmac_sha1 = ngx and ngx.hmac_sha1 or function(str, key) return require("crypto").hmac.digest("sha1", key, str, true) end +local hasposix , posix = pcall(require, "posix") + +local machineid +if hasposix then + machineid = posix.uname("%n") +else + machineid = assert(io.popen("uname -n")):read("*l") +end +machineid = md5(machineid):sub(1, 6) + +local function uint_to_hex(num, len, be) + local len = len or 4 + local be = be or 0 + local num = cbson.uint(num) + local raw = cbson.uint_to_raw(num, len, be) + local out = '' + for i = 1, #raw do + out = out .. string.format("%02x", raw:byte(i,i)) + end + return out +end + +local counter = 0 + +if not ngx then + math.randomseed(os.time()) + counter = math.random(100) +else + local resty_random = require "resty.random" + local resty_string = require "resty.string" + local strong_random = resty_random.bytes(4,true) + while strong_random == nil do + strong_random = resty_random.bytes(4,true) + end + counter = tonumber(resty_string.to_hex(strong_random), 16) +end + +local function generate_oid() + local pid = ngx and ngx.worker.pid() or nil + if not pid then + if hasposix then + pid = posix.getpid("pid") + else + pid = 1 + end + end + + pid = uint_to_hex(pid,2) + + counter = counter + 1 + local time = os.time() + + return uint_to_hex(time, 4, 1) .. machineid .. pid .. uint_to_hex(counter, 4, 1):sub(3,8) +end + +local function print_r(t, indent) + local indent=indent or '' + if #indent > 5 then return end + if type(t) ~= "table" then + print(t) + return + end + for key,value in pairs(t) do + io.write(indent,'[',tostring(key),']') + if type(value)=="table" then io.write(':\n') print_r(value,indent..'\t') + else io.write(' = ',tostring(value),'\n') end + end +end + +local function parse_uri(url) + -- initialize default parameters + local parsed = {} + -- empty url is parsed to nil + if not url or url == "" then return nil, "invalid url" end + -- remove whitespace + url = string.gsub(url, "%s", "") + -- get fragment + url = string.gsub(url, "#(.*)$", function(f) + parsed.fragment = f + return "" + end) + -- get scheme + url = string.gsub(url, "^([%w][%w%+%-%.]*)%:", + function(s) parsed.scheme = s; return "" end) + + -- get authority + local location + url = string.gsub(url, "^//([^/]*)", function(n) + location = n + return "" + end) + + -- get query stringing + url = string.gsub(url, "%?(.*)", function(q) + parsed.query_string = q + return "" + end) + -- get params + url = string.gsub(url, "%;(.*)", function(p) + parsed.params = p + return "" + end) + -- path is whatever was left + if url ~= "" then parsed.database = string.gsub(url,"^/([^/]*).*","%1") end + if not parsed.database or #parsed.database == 0 then parsed.database = "admin" end + + if not location then return parsed end + + location = string.gsub(location,"^([^@]*)@", + function(u) parsed.userinfo = u; return "" end) + + parsed.hosts = {} + string.gsub(location, "([^,]+)", function(u) + local pr = { host = "localhost", port = 27017 } + u = string.gsub(u, ":([^:]*)$", + function(p) pr.port = p; return "" end) + if u ~= "" then pr.host = u end + table.insert(parsed.hosts, pr) + end) + if #parsed.hosts == 0 then parsed.hosts = {{ host = "localhost", port = 27017 }} end + + parsed.query = {} + if parsed.query_string then + string.gsub(parsed.query_string, "([^&]+)", function(u) + u = string.gsub(u, "([^=]*)=([^=]*)$", + function(k,v) parsed.query[k] = v; return "" end) + end) + end + + local userinfo = parsed.userinfo + if not userinfo then return parsed end + userinfo = string.gsub(userinfo, ":([^:]*)$", + function(p) parsed.password = p; return "" end) + parsed.user = userinfo + return parsed +end + +local function xor_bytestr( a, b ) + local res = "" + for i=1,#a do + res = res .. string.char(bit.bxor(string.byte(a,i,i), string.byte(b, i, i))) + end + return res +end + +local function xor_bytestr( a, b ) + local res = "" + for i=1,#a do + res = res .. string.char(bit.bxor(string.byte(a,i,i), string.byte(b, i, i))) + end + return res +end + +-- A simple implementation of PBKDF2_HMAC_SHA1 +local function pbkdf2_hmac_sha1( pbkdf2_key, iterations, salt, len ) + local u1 = hmac_sha1(pbkdf2_key, salt .. "\0\0\0\1") + local ui = u1 + for i=1,iterations-1 do + u1 = hmac_sha1(pbkdf2_key, u1) + ui = xor_bytestr(ui, u1) + end + if #ui < len then + for i=1,len-(#ui) do + ui = string.char(0) .. ui + end + end + return ui +end + +-- not full implementation, but oh well +local function saslprep(username) + return string.gsub(string.gsub(username, '=', '=3D'), ',' , '=2C') +end + +local function pass_digest ( username , password ) + return md5(username .. ":mongo:" .. password) +end + +return { + parse_uri = parse_uri; + print_r = print_r; + pbkdf2_hmac_sha1 = pbkdf2_hmac_sha1; + saslprep = saslprep; + pass_digest = pass_digest; + xor_bytestr = xor_bytestr; + generate_oid = generate_oid; +} diff --git a/website/backend/app/libs/resty/redis/redis.lua b/website/backend/app/libs/resty/redis/redis.lua new file mode 100755 index 0000000..505341d --- /dev/null +++ b/website/backend/app/libs/resty/redis/redis.lua @@ -0,0 +1,439 @@ +-- Copyright (C) Yichun Zhang (agentzh) + + +local sub = string.sub +local byte = string.byte +local tcp = ngx.socket.tcp +local null = ngx.null +local type = type +local pairs = pairs +local unpack = unpack +local setmetatable = setmetatable +local tonumber = tonumber +local tostring = tostring +local rawget = rawget +--local error = error + + +local ok, new_tab = pcall(require, "table.new") +if not ok or type(new_tab) ~= "function" then + new_tab = function (narr, nrec) return {} end +end + + +local _M = new_tab(0, 54) + +_M._VERSION = '0.26' + + +local common_cmds = { + "get", "set", "mget", "mset", + "del", "incr", "decr", -- Strings + "llen", "lindex", "lpop", "lpush", + "lrange", "linsert", -- Lists + "hexists", "hget", "hset", "hmget", + --[[ "hmset", ]] "hdel", -- Hashes + "smembers", "sismember", "sadd", "srem", + "sdiff", "sinter", "sunion", -- Sets + "zrange", "zrangebyscore", "zrank", "zadd", + "zrem", "zincrby", -- Sorted Sets + "auth", "eval", "expire", "script", + "sort" -- Others +} + + +local sub_commands = { + "subscribe", "psubscribe" +} + + +local unsub_commands = { + "unsubscribe", "punsubscribe" +} + + +local mt = { __index = _M } + + +function _M.new(self) + local sock, err = tcp() + if not sock then + return nil, err + end + return setmetatable({ _sock = sock, _subscribed = false }, mt) +end + + +function _M.set_timeout(self, timeout) + local sock = rawget(self, "_sock") + if not sock then + return nil, "not initialized" + end + + return sock:settimeout(timeout) +end + + +function _M.connect(self, ...) + local sock = rawget(self, "_sock") + if not sock then + return nil, "not initialized" + end + + self._subscribed = false + + return sock:connect(...) +end + + +function _M.set_keepalive(self, ...) + local sock = rawget(self, "_sock") + if not sock then + return nil, "not initialized" + end + + if rawget(self, "_subscribed") then + return nil, "subscribed state" + end + + return sock:setkeepalive(...) +end + + +function _M.get_reused_times(self) + local sock = rawget(self, "_sock") + if not sock then + return nil, "not initialized" + end + + return sock:getreusedtimes() +end + + +local function close(self) + local sock = rawget(self, "_sock") + if not sock then + return nil, "not initialized" + end + + return sock:close() +end +_M.close = close + + +local function _read_reply(self, sock) + local line, err = sock:receive() + if not line then + if err == "timeout" and not rawget(self, "_subscribed") then + sock:close() + end + return nil, err + end + + local prefix = byte(line) + + if prefix == 36 then -- char '$' + -- print("bulk reply") + + local size = tonumber(sub(line, 2)) + if size < 0 then + return null + end + + local data, err = sock:receive(size) + if not data then + if err == "timeout" then + sock:close() + end + return nil, err + end + + local dummy, err = sock:receive(2) -- ignore CRLF + if not dummy then + return nil, err + end + + return data + + elseif prefix == 43 then -- char '+' + -- print("status reply") + + return sub(line, 2) + + elseif prefix == 42 then -- char '*' + local n = tonumber(sub(line, 2)) + + -- print("multi-bulk reply: ", n) + if n < 0 then + return null + end + + local vals = new_tab(n, 0) + local nvals = 0 + for i = 1, n do + local res, err = _read_reply(self, sock) + if res then + nvals = nvals + 1 + vals[nvals] = res + + elseif res == nil then + return nil, err + + else + -- be a valid redis error value + nvals = nvals + 1 + vals[nvals] = {false, err} + end + end + + return vals + + elseif prefix == 58 then -- char ':' + -- print("integer reply") + return tonumber(sub(line, 2)) + + elseif prefix == 45 then -- char '-' + -- print("error reply: ", n) + + return false, sub(line, 2) + + else + -- when `line` is an empty string, `prefix` will be equal to nil. + return nil, "unknown prefix: \"" .. tostring(prefix) .. "\"" + end +end + + +local function _gen_req(args) + local nargs = #args + + local req = new_tab(nargs * 5 + 1, 0) + req[1] = "*" .. nargs .. "\r\n" + local nbits = 2 + + for i = 1, nargs do + local arg = args[i] + if type(arg) ~= "string" then + arg = tostring(arg) + end + + req[nbits] = "$" + req[nbits + 1] = #arg + req[nbits + 2] = "\r\n" + req[nbits + 3] = arg + req[nbits + 4] = "\r\n" + + nbits = nbits + 5 + end + + -- it is much faster to do string concatenation on the C land + -- in real world (large number of strings in the Lua VM) + return req +end + + +local function _do_cmd(self, ...) + local args = {...} + + local sock = rawget(self, "_sock") + if not sock then + return nil, "not initialized" + end + + local req = _gen_req(args) + + local reqs = rawget(self, "_reqs") + if reqs then + reqs[#reqs + 1] = req + return + end + + -- print("request: ", table.concat(req)) + + local bytes, err = sock:send(req) + if not bytes then + return nil, err + end + + return _read_reply(self, sock) +end + + +local function _check_subscribed(self, res) + if type(res) == "table" + and (res[1] == "unsubscribe" or res[1] == "punsubscribe") + and res[3] == 0 + then + self._subscribed = false + end +end + + +function _M.read_reply(self) + local sock = rawget(self, "_sock") + if not sock then + return nil, "not initialized" + end + + if not rawget(self, "_subscribed") then + return nil, "not subscribed" + end + + local res, err = _read_reply(self, sock) + _check_subscribed(self, res) + + return res, err +end + + +for i = 1, #common_cmds do + local cmd = common_cmds[i] + + _M[cmd] = + function (self, ...) + return _do_cmd(self, cmd, ...) + end +end + + +for i = 1, #sub_commands do + local cmd = sub_commands[i] + + _M[cmd] = + function (self, ...) + self._subscribed = true + return _do_cmd(self, cmd, ...) + end +end + + +for i = 1, #unsub_commands do + local cmd = unsub_commands[i] + + _M[cmd] = + function (self, ...) + local res, err = _do_cmd(self, cmd, ...) + _check_subscribed(self, res) + return res, err + end +end + + +function _M.hmset(self, hashname, ...) + if select('#', ...) == 1 then + local t = select(1, ...) + + local n = 0 + for k, v in pairs(t) do + n = n + 2 + end + + local array = new_tab(n, 0) + + local i = 0 + for k, v in pairs(t) do + array[i + 1] = k + array[i + 2] = v + i = i + 2 + end + -- print("key", hashname) + return _do_cmd(self, "hmset", hashname, unpack(array)) + end + + -- backwards compatibility + return _do_cmd(self, "hmset", hashname, ...) +end + + +function _M.init_pipeline(self, n) + self._reqs = new_tab(n or 4, 0) +end + + +function _M.cancel_pipeline(self) + self._reqs = nil +end + + +function _M.commit_pipeline(self) + local reqs = rawget(self, "_reqs") + if not reqs then + return nil, "no pipeline" + end + + self._reqs = nil + + local sock = rawget(self, "_sock") + if not sock then + return nil, "not initialized" + end + + local bytes, err = sock:send(reqs) + if not bytes then + return nil, err + end + + local nvals = 0 + local nreqs = #reqs + local vals = new_tab(nreqs, 0) + for i = 1, nreqs do + local res, err = _read_reply(self, sock) + if res then + nvals = nvals + 1 + vals[nvals] = res + + elseif res == nil then + if err == "timeout" then + close(self) + end + return nil, err + + else + -- be a valid redis error value + nvals = nvals + 1 + vals[nvals] = {false, err} + end + end + + return vals +end + + +function _M.array_to_hash(self, t) + local n = #t + -- print("n = ", n) + local h = new_tab(0, n / 2) + for i = 1, n, 2 do + h[t[i]] = t[i + 1] + end + return h +end + + +-- this method is deperate since we already do lazy method generation. +function _M.add_commands(...) + local cmds = {...} + for i = 1, #cmds do + local cmd = cmds[i] + _M[cmd] = + function (self, ...) + return _do_cmd(self, cmd, ...) + end + end +end + + +setmetatable(_M, {__index = function(self, cmd) + local method = + function (self, ...) + return _do_cmd(self, cmd, ...) + end + + -- cache the lazily generated method in our + -- module table + _M[cmd] = method + return method +end}) + + +return _M diff --git a/website/backend/app/libs/resty/smtp.lua b/website/backend/app/libs/resty/smtp.lua new file mode 100755 index 0000000..034bcf0 --- /dev/null +++ b/website/backend/app/libs/resty/smtp.lua @@ -0,0 +1,321 @@ +----------------------------------------------------------------------------- +-- SMTP client support for the Lua language. +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: smtp.lua,v 1.46 2007/03/12 04:08:40 diego Exp $ +----------------------------------------------------------------------------- +-- Author: Hungpu DU +-- ChangeLog: +-- * 2014/04/06 03:50:15 - Modified for lua-nginx-module with pure Lua +----------------------------------------------------------------------------- + + +local base = _G +local coroutine = require("coroutine") +local string = require("string") +local math = require("math") +local os = require("os") + +local mime = require("resty.smtp.mime") +local ltn12 = require("resty.smtp.ltn12") +local tp = require("resty.smtp.tp") +local misc = require("resty.smtp.misc") + +module("resty.smtp") + + + +_VERSION = "0.0.3" + +-- timeout for connection +TIMEOUT = 6000 +-- default server used to send e-mails +SERVER = "localhost" +-- default port +PORT = 25 +-- domain used in HELO command and default sendmail +-- If we are under a CGI, try to get from environment +DOMAIN = "localhost" +-- default time zone (means we don"t know) +ZONE = "-0000" + + +local metat = { __index= {} } + +function metat.__index:greet(domain) + self.try(self.tp:expect("2..")) + self.try(self.tp:command("EHLO", domain or DOMAIN)) + + return misc.skip(1, self.try(self.tp:expect("2.."))) +end + + +function metat.__index:mail(from) + self.try(self.tp:command("MAIL", "FROM:" .. from)) + + return self.try(self.tp:expect("2..")) +end + + +function metat.__index:rcpt(to) + self.try(self.tp:command("RCPT", "TO:" .. to)) + + return self.try(self.tp:expect("2..")) +end + + +function metat.__index:data(src, step) + self.try(self.tp:command("DATA")) + self.try(self.tp:expect("3..")) + self.try(self.tp:source(src, step)) + self.try(self.tp:send("\r\n.\r\n")) + + return self.try(self.tp:expect("2..")) +end + + +function metat.__index:quit() + self.try(self.tp:command("QUIT")) + + return self.try(self.tp:expect("2..")) +end + + +function metat.__index:close() + return self.tp:close() +end + + +function metat.__index:login(user, password) + self.try(self.tp:command("AUTH", "LOGIN")) + self.try(self.tp:expect("3..")) + self.try(self.tp:command(mime.b64(user))) + self.try(self.tp:expect("3..")) + self.try(self.tp:command(mime.b64(password))) + + return self.try(self.tp:expect("2..")) +end + + +function metat.__index:plain(user, password) + local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password) + self.try(self.tp:command("AUTH", auth)) + return self.try(self.tp:expect("2..")) +end + + +function metat.__index:auth(user, password, ext) + if not user or not password then return 1 end + + if string.find(ext, "AUTH[^\n]+LOGIN") then + return self:login(user, password) + + elseif string.find(ext, "AUTH[^\n]+PLAIN") then + return self:plain(user, password) + + else + self.try(nil, "authentication not supported") + end +end + + +-- send message or throw an exception +function metat.__index:send(mailt) + self:mail(mailt.from) + + if base.type(mailt.rcpt) == "table" then + for i, v in base.ipairs(mailt.rcpt) do + self:rcpt(v) + end + + else + self:rcpt(mailt.rcpt) + end + + self:data(ltn12.source.chain(mailt.source, mime.stuff()), mailt.step) +end + + +-- private methods +-- +function open(server, port, timeout, create, ssl) + local tp = misc.try(tp.connect(server, port, timeout, create, ssl)) + local session = base.setmetatable({tp= tp}, metat) + + -- make sure tp is closed if we get an exception + session.try = misc.newtry(function() + session:close() + end) + return session +end + + +-- convert headers to lowercase +local function lower_headers(headers) + local lower = {} + + for i,v in base.pairs(headers or lower) do + lower[string.lower(i)] = v + end + + return lower +end + + +-- returns a hopefully unique mime boundary +local seqno = 0 +local function newboundary() + seqno = seqno + 1 + + return string.format("%s%05d==%05u", os.date("%d%m%Y%H%M%S"), + math.random(0, 99999), seqno) +end + + +-- send_message forward declaration +local send_message + +-- yield the headers all at once, it"s faster +local function send_headers(headers) + local h = {} + + for k, v in base.pairs(headers) do + base.table.insert(h, base.table.concat({k, v}, ": ")) + end + base.table.insert(h, "\r\n") + + coroutine.yield(base.table.concat(h, "\r\n")) +end + + +-- yield multipart message body from a multipart message table +local function send_multipart(mesgt) + -- make sure we have our boundary and send headers + local bd = newboundary() + local headers = lower_headers(mesgt.headers or {}) + + headers["content-type"] = headers["content-type"] or "multipart/mixed" + headers["content-type"] = headers["content-type"] .. + '; boundary="' .. bd .. '"' + + send_headers(headers) + + -- send preamble + if mesgt.body.preamble then + coroutine.yield(mesgt.body.preamble) + coroutine.yield("\r\n") + end + + -- send each part separated by a boundary + for i, m in base.ipairs(mesgt.body) do + coroutine.yield("\r\n--" .. bd .. "\r\n") + send_message(m) + end + + -- send last boundary + coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n") + + -- send epilogue + if mesgt.body.epilogue then + coroutine.yield(mesgt.body.epilogue) + coroutine.yield("\r\n") + end +end + + +-- yield message body from a source +local function send_source(mesgt) + -- make sure we have a content-type + local headers = lower_headers(mesgt.headers or {}) + + headers["content-type"] = headers["content-type"] or + 'text/plain; charset="iso-8859-1"' + + send_headers(headers) + + -- send body from source + while true do + local chunk, err = mesgt.body() + if err then coroutine.yield(nil, err) + elseif chunk then coroutine.yield(chunk) + else break end + end +end + + +-- yield message body from a string +local function send_string(mesgt) + -- make sure we have a content-type + local headers = lower_headers(mesgt.headers or {}) + + headers["content-type"] = headers["content-type"] or + 'text/plain; charset="iso-8859-1"' + + send_headers(headers) + + -- send body from string + coroutine.yield(mesgt.body) +end + + +-- message source +function send_message(mesgt) + if base.type(mesgt.body) == "table" then + send_multipart(mesgt) + + elseif base.type(mesgt.body) == "function" then + send_source(mesgt) + + else + send_string(mesgt) + end +end + + +-- set defaul headers +local function adjust_headers(mesgt) + -- to eliminate duplication for following headers + local lower = lower_headers(mesgt.headers) + + lower["date"] = lower["date"] or + os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or ZONE) + lower["x-mailer"] = lower["x-mailer"] or VERSION + -- this can"t be overriden + lower["mime-version"] = "1.0" + + return lower +end + + +function message(mesgt) + mesgt.headers = adjust_headers(mesgt) + + -- create and return message source + local co = coroutine.create(function() send_message(mesgt) end) + + return function() + local ok, a, b = coroutine.resume(co) + if ok then return a, b + else return nil, a end + end +end + + +-- public methods +-- +send = misc.except(function(mailt) + local session = open(mailt.server or SERVER, mailt.port or PORT, + mailt.timeout or TIMEOUT, + mailt.create or base.ngx.socket.tcp, + mailt.ssl or {enable= false, verify_cert= false}) + + local ext = session:greet(mailt.domain) + + session:auth(mailt.user, mailt.password, ext) + session:send(mailt) + session:quit() + + return session:close() +end) + + diff --git a/website/backend/app/libs/resty/smtp/base64.lua b/website/backend/app/libs/resty/smtp/base64.lua new file mode 100755 index 0000000..b0ecd95 --- /dev/null +++ b/website/backend/app/libs/resty/smtp/base64.lua @@ -0,0 +1,142 @@ +----------------------------------------------------------------------------- +-- base64 convertion routines for the Lua language +-- Author: Diego Nehab +-- Date: 26/12/2000 +-- Conforms to: RFC 1521 +----------------------------------------------------------------------------- +-- Author: Hungpu DU +-- Date: 2014/04/06 +-- ChangeLog: +-- * Taken from LuaSocket 1.1 and modified for Lua 5.1 +-- * Modified to be suitable for b64 filter + +local math = require("math") +local string = require("string") + +module("resty.smtp.base64") + + + +local t64 = { + [0] = 'A', [1] = 'B', [2] = 'C', [3] = 'D', [4] = 'E', [5] = 'F', [6] = 'G', + [7] = 'H', [8] = 'I', [9] = 'J', [10] = 'K', [11] = 'L', [12] = 'M', + [13] = 'N', [14] = 'O', [15] = 'P', [16] = 'Q', [17] = 'R', [18] = 'S', + [19] = 'T', [20] = 'U', [21] = 'V', [22] = 'W', [23] = 'X', [24] = 'Y', + [25] = 'Z', [26] = 'a', [27] = 'b', [28] = 'c', [29] = 'd', [30] = 'e', + [31] = 'f', [32] = 'g', [33] = 'h', [34] = 'i', [35] = 'j', [36] = 'k', + [37] = 'l', [38] = 'm', [39] = 'n', [40] = 'o', [41] = 'p', [42] = 'q', + [43] = 'r', [44] = 's', [45] = 't', [46] = 'u', [47] = 'v', [48] = 'w', + [49] = 'x', [50] = 'y', [51] = 'z', [52] = '0', [53] = '1', [54] = '2', + [55] = '3', [56] = '4', [57] = '5', [58] = '6', [59] = '7', [60] = '8', + [61] = '9', [62] = '+', [63] = '/', +} + +local f64 = { + ['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4, ['F'] = 5, ['G'] = 6, + ['H'] = 7, ['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11, ['M'] = 12, + ['N'] = 13, ['O'] = 14, ['P'] = 15, ['Q'] = 16, ['R'] = 17, ['S'] = 18, + ['T'] = 19, ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23, ['Y'] = 24, + ['Z'] = 25, ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29, ['e'] = 30, + ['f'] = 31, ['g'] = 32, ['h'] = 33, ['i'] = 34, ['j'] = 35, ['k'] = 36, + ['l'] = 37, ['m'] = 38, ['n'] = 39, ['o'] = 40, ['p'] = 41, ['q'] = 42, + ['r'] = 43, ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47, ['w'] = 48, + ['x'] = 49, ['y'] = 50, ['z'] = 51, ['0'] = 52, ['1'] = 53, ['2'] = 54, + ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59, ['8'] = 60, + ['9'] = 61, ['+'] = 62, ['/'] = 63, +} + + +local t2f = function(a, b, c) + local s = a:byte() * 65536 + b:byte() * 256 + c:byte() + local ca, cb, cc, cd + + cd = math.fmod(s, 64) + s = (s - cd) / 64 + cc = math.fmod(s, 64) + s = (s - cc) / 64 + cb = math.fmod(s, 64) + ca = (s - cb) / 64 + + return t64[ca] .. t64[cb] .. t64[cc] .. t64[cd] +end + + +local f2t = function(a, b, c, d) + local s, ca, cb, cc + + if d ~= "=" then + s = f64[a] * 262144 + f64[b] * 4096 + f64[c] * 64 + f64[d] + + cc = math.fmod(s, 256) + s = (s - cc) / 256 + cb = math.fmod(s, 256) + ca = (s - cb) / 256 + + return string.char(ca, cb, cc) + + elseif c ~= "=" then + s = f64[a] * 1024 + f64[b] * 16 + f64[c] / 4 + cb = math.fmod(s, 256) + ca = (s - cb) / 256 + + return string.char(ca, cb) + + else + s = f64[a] * 4 + f64[b] / 16 + ca = math.fmod(s, 256) + return string.char(ca) + end +end + + +function pad(chunk) + local s, a, b, ca, cb, cc + + if #chunk == 0 then return "" end + + _, _, a, b = chunk:find("(.?)(.?)") + if b == "" then + s = a:byte() * 16 + cb = math.fmod(s, 64) + ca = (s - cb)/64 + return t64[ca] .. t64[cb] .. "==" + + else + s = a:byte() * 1024 + b:byte() * 4 + cc = math.fmod(s, 64) + s = (s - cc) / 64 + cb = math.fmod(s, 64) + ca = (s - cb)/64 + return t64[ca] .. t64[cb] .. t64[cc] .. "=" + end +end + + +function encode(chunk) + local l = #chunk + local r = math.fmod(l, 3) + + l = l - r + if l <= 0 then return "", chunk end + + return string.gsub(chunk:sub(1, l), "(.)(.)(.)", t2f), chunk:sub(l + 1) +end + +function decode(chunk) + local l = #chunk + local r = math.fmod(l, 4) + + l = l - r + if l <= 0 then return "", chunk end + + return string.gsub(chunk:sub(1, l), "(.)(.)(.)(.)", f2t), chunk:sub(l + 1) +end + + +-- for Encoded Word +-- +function qencode(chunk) return encode(chunk) end +function qdecode(chunk) return decode(chunk) end +function qpad(chunk) return pad(chunk) end + + diff --git a/website/backend/app/libs/resty/smtp/ltn12.lua b/website/backend/app/libs/resty/smtp/ltn12.lua new file mode 100755 index 0000000..9267823 --- /dev/null +++ b/website/backend/app/libs/resty/smtp/ltn12.lua @@ -0,0 +1,297 @@ +----------------------------------------------------------------------------- +-- LTN12 - Filters, sources, sinks and pumps. +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: ltn12.lua,v 1.31 2006/04/03 04:45:42 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module +----------------------------------------------------------------------------- +local base = _G +local string = require("string") +local table = require("table") + +module("resty.smtp.ltn12") + + + +filter = {} +source = {} +sink = {} +pump = {} + +-- 2048 seems to be better in windows... +BLOCKSIZE = 2048 +_VERSION = "LTN12 1.0.1" + +----------------------------------------------------------------------------- +-- Filter stuff +----------------------------------------------------------------------------- +-- returns a high level filter that cycles a low-level filter +function filter.cycle(low, ctx, extra) + base.assert(low) + return function(chunk) + local ret + ret, ctx = low(ctx, chunk, extra) + return ret + end +end + +-- chains a bunch of filters together +-- (thanks to Wim Couwenberg) +function filter.chain(...) + local arg = {...} + local n = #arg + local top, index = 1, 1 + local retry = "" + return function(chunk) + retry = chunk and retry + while true do + if index == top then + chunk = arg[index](chunk) + if chunk == "" or top == n then return chunk + elseif chunk then index = index + 1 + else + top = top+1 + index = top + end + else + chunk = arg[index](chunk or "") + if chunk == "" then + index = index - 1 + chunk = retry + elseif chunk then + if index == n then return chunk + else index = index + 1 end + else base.error("filter returned inappropriate nil") end + end + end + end +end + +----------------------------------------------------------------------------- +-- Source stuff +----------------------------------------------------------------------------- +-- create an empty source +local function empty() + return nil +end + +function source.empty() + return empty +end + +-- returns a source that just outputs an error +function source.error(err) + return function() + return nil, err + end +end + +-- creates a file source +function source.file(handle, io_err) + if handle then + return function() + local chunk = handle:read(BLOCKSIZE) + if not chunk then handle:close() end + return chunk + end + else return source.error(io_err or "unable to open file") end +end + +-- turns a fancy source into a simple source +function source.simplify(src) + base.assert(src) + return function() + local chunk, err_or_new = src() + src = err_or_new or src + if not chunk then return nil, err_or_new + else return chunk end + end +end + +-- creates string source +function source.string(s) + if s then + local i = 1 + return function() + local chunk = string.sub(s, i, i+BLOCKSIZE-1) + i = i + BLOCKSIZE + if chunk ~= "" then return chunk + else return nil end + end + else return source.empty() end +end + +-- creates rewindable source +function source.rewind(src) + base.assert(src) + local t = {} + return function(chunk) + if not chunk then + chunk = table.remove(t) + if not chunk then return src() + else return chunk end + else + table.insert(t, chunk) + end + end +end + +function source.chain(src, f) + base.assert(src and f) + local last_in, last_out = "", "" + local state = "feeding" + local err + return function() + if not last_out then + base.error('source is empty!', 2) + end + while true do + if state == "feeding" then + last_in, err = src() + if err then return nil, err end + last_out = f(last_in) + if not last_out then + if last_in then + base.error('filter returned inappropriate nil') + else + return nil + end + elseif last_out ~= "" then + state = "eating" + if last_in then last_in = "" end + return last_out + end + else + last_out = f(last_in) + if last_out == "" then + if last_in == "" then + state = "feeding" + else + base.error('filter returned ""') + end + elseif not last_out then + if last_in then + base.error('filter returned inappropriate nil') + else + return nil + end + else + return last_out + end + end + end + end +end + +-- creates a source that produces contents of several sources, one after the +-- other, as if they were concatenated +-- (thanks to Wim Couwenberg) +function source.cat(...) + local arg = {...} + local src = table.remove(arg, 1) + return function() + while src do + local chunk, err = src() + if chunk then return chunk end + if err then return nil, err end + src = table.remove(arg, 1) + end + end +end + +----------------------------------------------------------------------------- +-- Sink stuff +----------------------------------------------------------------------------- +-- creates a sink that stores into a table +function sink.table(t) + t = t or {} + local f = function(chunk, err) + if chunk then table.insert(t, chunk) end + return 1 + end + return f, t +end + +-- turns a fancy sink into a simple sink +function sink.simplify(snk) + base.assert(snk) + return function(chunk, err) + local ret, err_or_new = snk(chunk, err) + if not ret then return nil, err_or_new end + snk = err_or_new or snk + return 1 + end +end + +-- creates a file sink +function sink.file(handle, io_err) + if handle then + return function(chunk, err) + if not chunk then + handle:close() + return 1 + else return handle:write(chunk) end + end + else return sink.error(io_err or "unable to open file") end +end + +-- creates a sink that discards data +local function null() + return 1 +end + +function sink.null() + return null +end + +-- creates a sink that just returns an error +function sink.error(err) + return function() + return nil, err + end +end + +-- chains a sink with a filter +function sink.chain(f, snk) + base.assert(f and snk) + return function(chunk, err) + if chunk ~= "" then + local filtered = f(chunk) + local done = chunk and "" + while true do + local ret, snkerr = snk(filtered, err) + if not ret then return nil, snkerr end + if filtered == done then return 1 end + filtered = f(done) + end + else return 1 end + end +end + +----------------------------------------------------------------------------- +-- Pump stuff +----------------------------------------------------------------------------- +-- pumps one chunk from the source to the sink +function pump.step(src, snk) + local chunk, src_err = src() + local ret, snk_err = snk(chunk, src_err) + if chunk and ret then return 1 + else return nil, src_err or snk_err end +end + +-- pumps all data from a source to a sink, using a step function +function pump.all(src, snk, step) + base.assert(src and snk) + step = step or pump.step + while true do + local ret, err = step(src, snk) + if not ret then + if err then return nil, err + else return 1 end + end + end +end + diff --git a/website/backend/app/libs/resty/smtp/mime-core.lua b/website/backend/app/libs/resty/smtp/mime-core.lua new file mode 100755 index 0000000..0911c27 --- /dev/null +++ b/website/backend/app/libs/resty/smtp/mime-core.lua @@ -0,0 +1,271 @@ +local base = _G +local string = require("string") +local base64 = require("resty.smtp.base64") +local qpcore = require("resty.smtp.qp") + +module("resty.smtp.mime") + + + +-- FIXME following mime-relative string operations are quite inefficient +-- compared with original C version, maybe FFI can help? +-- +-- base64 +-- +function b64(ctx, chunk, extra) + local part1, part2 + + if not ctx then return nil, nil end + + -- remaining data from last round + part1, ctx = base64.encode(ctx) + + if not chunk then + part1 = part1 .. base64.pad(ctx) + + if #part1 == 0 then return nil, nil + else return part1, nil end + end + + -- second part + part2, ctx = base64.encode(ctx .. chunk) + + return part1 .. part2, ctx +end + +function unb64(ctx, chunk, extra) + local part1, part2 + + if not ctx then return nil, nil end + + -- remaining data from last round + part1, ctx = base64.decode(ctx) + + if not chunk then + if #part1 == 0 then return nil, nil + else return part1, nil end + end + + -- second part + part2, ctx = base64.decode(ctx .. chunk) + + return part1 .. part2, ctx +end + + +-- quoted-printable +-- +function qp(ctx, chunk, extra) + local part1, part2, marker + + if not ctx then return nil, nil end + + marker = extra or "\r\n" + part1, ctx = qpcore.encode(ctx, marker) + + if not chunk then + part1 = part1 .. qpcore.pad(ctx) + + if #part1 == 0 then return nil, nil + else return part1, nil end + end + + -- second part + part2, ctx = qpcore.encode(ctx .. chunk, marker) + + return part1 .. part2, ctx +end + +function unqp(ctx, chunk, extra) + local part1, part2 + + if not ctx then return nil, nil end + + -- remaining data from last round + part1, ctx = qpcore.decode(ctx) + + if not chunk then + if #part1 == 0 then return nil, nil + else return part1, nil end + end + + -- second part + part2, ctx = qpcore.decode(ctx .. chunk) + + return part1 .. part2, ctx +end + + +-- line-wrap +-- +function wrp(ctx, chunk, extra) + -- `ctx` shows how many more bytes current line can still hold + -- before reach the limit `length` + local buffer, length = "", extra or 76 + + if not chunk then + -- last line already has some chars except \r\n + if ctx < length then return buffer .. "\r\n", length + else return nil, length end + end + + for i = 1, #chunk do + local char = chunk:sub(i, i) + + if char == '\r' then + -- take it as part of "\r\n" + elseif char == '\n' then + buffer, ctx = buffer .. "\r\n", length + else + if ctx <= 0 then -- hit the limit + buffer, ctx = buffer .. "\r\n", length + end + + buffer, ctx = buffer .. char, ctx - 1 + end + end + + return buffer, ctx +end + +function qpwrp(ctx, chunk, extra) + -- `ctx` shows how many more bytes current line can still hold + -- before reach the limit `length` + local buffer, length = "", extra or 76 + + if not chunk then + -- last line already has some chars except \r\n + if ctx < length then return buffer .. "=\r\n", length + else return nil, length end + + end + + for i = 1, #chunk do + local char = chunk:sub(i, i) + + if char == '\r' then + -- take it as part of "\r\n" + elseif char == '\n' then + buffer, ctx = buffer .. "\r\n", length + elseif char == '=' then + if ctx <= 3 then + buffer, ctx = buffer .. "=\r\n", length + end + + buffer, ctx = buffer .. char, ctx - 1 + + else + if ctx <= 1 then + buffer, ctx = buffer .. "=\r\n", length + end + + buffer, ctx = buffer .. char, ctx - 1 + end + end + + return buffer, ctx +end + + +-- encoded word +-- +function ew(ctx, chunk, extra) + local part0, part1, part2 = "", "", "" + local c, e, f + + base.assert(base.type(extra) == "table") + + c = extra.charset or "utf-8" + e = extra.encoding or "B" + m = (e == "Q") and qpcore or base64 + + -- TODO not support Q-encoding yet + base.assert(e == "B") + + if extra.initial == nil or extra.initial then + part0 = string.format("=?%s?%s?", c, e) + extra.initial = false + end + + part1, ctx = m.qencode(ctx, true) + + if not chunk then + part1 = part1 .. m.qpad(ctx, true) + return part0 .. part1 .. "?=", nil + end + + part2, ctx = m.qencode(ctx .. chunk, true) + + return part0 .. part1 .. part2, ctx +end + +-- +-- extra - the charset to converted to +function unew(ctx, chunk, extra) + -- TODO + -- This one needs a little more work, because we have to decode + -- `chunk` with both specified encoding and charset on the fly. + -- +end + + +-- dot +-- +function dot(ctx, chunk, extra) + local buffer = "" + + if not chunk then return nil, 2 end + + for i = 1, #chunk do + local char = string.char(string.byte(chunk, i)) + + buffer = buffer .. char + + if char == '\r' then + ctx = 1 + elseif char == '\n' then + ctx = (ctx == 1) and 2 or 0 + elseif char == "." then + if ctx == 2 then buffer = buffer .. "." end + ctx = 0 + else + ctx = 0 + end + end + + return buffer, ctx +end + + +-- eol +-- +function eol(ctx, chunk, marker) + local buffer = "" + + if not chunk then return nil, 0 end + + local eolcandidate = function(char) + return (char == '\r') or (char == '\n') + end + + for i = 1, #chunk do + local char = string.char(string.byte(chunk, i)) + + if eolcandidate(char) then + if eolcandidate(ctx) then + if char == ctx then buffer = buffer .. marker end + ctx = 0 + else + buffer = buffer .. marker + ctx = char + end + + else + buffer = buffer .. char + ctx = 0 + end + end + + return buffer, ctx +end + diff --git a/website/backend/app/libs/resty/smtp/mime.lua b/website/backend/app/libs/resty/smtp/mime.lua new file mode 100755 index 0000000..a155dca --- /dev/null +++ b/website/backend/app/libs/resty/smtp/mime.lua @@ -0,0 +1,103 @@ +----------------------------------------------------------------------------- +-- MIME support for the Lua language. +-- Author: Diego Nehab +-- Conforming to RFCs 2045-2049 +-- RCS ID: $Id: mime.lua,v 1.29 2007/06/11 23:44:54 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local io = require("io") +local string = require("string") + +require("resty.smtp.mime-core") +local ltn12 = require("resty.smtp.ltn12") + +module("resty.smtp.mime") + + + +-- encode, decode and wrap algorithm tables +encodet = {} +decodet = {} +wrapt = {} + +-- creates a function that chooses a filter by name from a given table +local function choose(table) + return function(name, opt1, opt2) + if base.type(name) ~= "string" then + name, opt1, opt2 = "default", name, opt1 + end + local f = table[name or "nil"] + if not f then + base.error("unknown key (" .. base.tostring(name) .. ")", 3) + else return f(opt1, opt2) end + end +end + +-- define the encoding filters +encodet['base64'] = function() + return ltn12.filter.cycle(b64, "") +end + +encodet['quoted-printable'] = function(mode) + return ltn12.filter.cycle(qp, "", + (mode == "binary") and "=0D=0A" or "\r\n") +end + +encodet['encoded-word'] = function(charset, encoding) + return ltn12.filter.cycle(ew, "", + { charset= charset or "utf-8", encoding= encoding or "B" }) +end + +-- define the decoding filters +decodet['base64'] = function() + return ltn12.filter.cycle(unb64, "") +end + +decodet['quoted-printable'] = function() + return ltn12.filter.cycle(unqp, "") +end + +decodet['encoded-word'] = function(encoding) + return ltn12.filter.cycle(unew, "", + { encoding= encoding or "Q" }) +end + +local function format(chunk) + if chunk then + if chunk == "" then return "''" + else return string.len(chunk) end + else return "nil" end +end + +-- define the line-wrap filters +wrapt['text'] = function(length) + length = length or 76 + return ltn12.filter.cycle(wrp, length, length) +end +wrapt['base64'] = wrapt['text'] +wrapt['default'] = wrapt['text'] + +wrapt['quoted-printable'] = function() + return ltn12.filter.cycle(qpwrp, 76, 76) +end + +-- function that choose the encoding, decoding or wrap algorithm +encode = choose(encodet) +decode = choose(decodet) +wrap = choose(wrapt) + +-- define the end-of-line normalization filter +function normalize(marker) + if not marker then marker = '\r\n' end + return ltn12.filter.cycle(eol, 0, marker) +end + +-- high level stuffing filter +function stuff() + return ltn12.filter.cycle(dot, 2) +end + diff --git a/website/backend/app/libs/resty/smtp/misc.lua b/website/backend/app/libs/resty/smtp/misc.lua new file mode 100755 index 0000000..ccf06d3 --- /dev/null +++ b/website/backend/app/libs/resty/smtp/misc.lua @@ -0,0 +1,37 @@ +local base = _G + +module("resty.smtp.misc") + + +function skip(amount, ...) + return base.unpack({ ... }, amount + 1) +end + + +function newtry(atexit) + return function(...) + local ret, err = base.select(1, ...), base.select(2, ...) + + if ret then return ... end + if base.type(atexit) == "function" then atexit() end + + base.error(err, 2) + -- never be here + return ret + end +end + + +function except(func) + return function(...) + local ok, ret = base.pcall(func, ...) + + if not ok then return nil, ret + else return ret end + end +end + + +try = newtry() + + diff --git a/website/backend/app/libs/resty/smtp/qp.lua b/website/backend/app/libs/resty/smtp/qp.lua new file mode 100755 index 0000000..2d99f12 --- /dev/null +++ b/website/backend/app/libs/resty/smtp/qp.lua @@ -0,0 +1,222 @@ +local base = _G +local math = require("math") +local table = require("table") +local string = require("string") + +module("resty.smtp.qp") + + + +--[[ + +[Quoted-Printable Rules](http://en.wikipedia.org/wiki/Quoted-printable) + +* "All characters except printable ASCII characters" or "end of line characters" + must be encoded. + +* All printable ASCII characters (decimal 33-126) may be represented by + themselves, except "=" (decimal 61) + +* ASCII tab (decimal 9) and space (decimal 32) may be represented by themselves, + except if these characters would appear at the end of the encoded line. In + that case, they would need to be escaped as "=09" or "=20" (what we use), or + be followed by a "=" (soft line break). + +* If the data being encoded contains meaningful line breaks, they must be + encoded as an ASCII CRLF sequence. Conversely, if byte value 13 and 10 have + meanings other than end of line (in media types, for example), they must + be encoded as "=0D" and "=0A" respectively. + +* Lines of Quoted-Printable encoded data must not be longer than 76 characters. + To satisfy this requirement without altering the encoded text, + _soft line breaks_ consists of an "=" at the end of an encoded line, and does + not appear as a line break in the decoded text. + + +Encoded-Word - A slightly modified version of Quoted-Printable used in message +headers. + + +[RFC 2045](http://tools.ietf.org/html/rfc2045#page-19) + +--]] + +local QP_PLAIN = 0 +local QP_QUOTE = 1 +local QP_IF_LAST = 3 +local QP_BYTE_CR = 13 -- '\r' +local QP_BYTE_LF = 10 -- '\n' + + +qpte = { +-- 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 2, 1, 1, -- 0 - 15 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 16 - 31 + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 32 - 47 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -- 48 - 63 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 64 - 79 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 80 - 95 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 96 - 111 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -- 112 - 127 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 128 - 143 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 144 - 159 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 160 - 175 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 176 - 191 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 192 - 207 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 208 - 223 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 224 - 239 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 240 - 255 +} + + + +qptd = { +} + + +local HEX_BASE = "0123456789ABCDEF" + +local quote = function(byte) + local f, s = math.floor(byte/16) + 1, math.fmod(byte, 16) + 1 + return table.concat({ '=', HEX_BASE:sub(f, f), HEX_BASE:sub(s, s) }) +end + + +local HEX_LOOKUP = { + ['0']= 0, ['1']= 1, ['2']= 2, ['3']= 3, + ['4']= 4, ['5']= 5, ['6']= 6, ['7']= 7, + ['8']= 8, ['9']= 9, ['a']= 10, ['b']= 11, + ['c']= 12, ['d']= 13, ['e']= 14, ['f']= 15, + ['A']= 10, ['B']= 11, + ['C']= 12, ['D']= 13, ['E']= 14, ['F']= 15, +} + +local unquote = function(fp, sp) + local hp, lp = HEX_LOOKUP[fp], HEX_LOOKUP[sp] + + if not hp or not lp then return nil + else return string.char(hp * 16 + lp) end +end + +local printable = function(char) + local byte = string.byte(char) + return (byte > 31) and (byte < 127) +end + + +function pad(chunk) + local buffer, byte = "", 0 + + for i = 1, #chunk do + byte = string.byte(chunk, i) + + if qpte[byte + 1] == QP_PLAIN then + buffer = buffer .. string.char(byte) + else + buffer = buffer .. quote(byte) + end + end + + -- soft break + if #buffer > 0 then buffer = buffer .. "=\r\n" end + + return buffer +end + + +function encode(chunk, marker) + local atom, buffer = {}, "" + + for i = 1, #chunk do + table.insert(atom, string.byte(chunk, i)) + + repeat + local shift = 1 + + if atom[1] == QP_BYTE_CR then + if #atom < 2 then -- need more + break + elseif atom[2] == QP_BYTE_LF then + buffer, shift = buffer .. marker, 2 + else + buffer = buffer .. quote(atom[1]) + end + + elseif qpte[atom[1] + 1] == QP_IF_LAST then + if #atom < 3 then -- need more + break + elseif atom[2] == QP_BYTE_CR and atom[3] == QP_BYTE_LF then + buffer, shift = buffer .. quote(atom[1]) .. marker, 3 + else -- space not in the end + buffer = buffer .. string.char(atom[1]) + end + + elseif qpte[atom[1] + 1] == QP_QUOTE then + buffer = buffer .. quote(atom[1]) + + else -- printable char + buffer = buffer .. string.char(atom[1]) + end + + -- shift out used chars + for i = 1, 3 do atom[i] = atom[i + shift] end + + until #atom == 0 + + end + + for i = 1, 3 do + atom[i] = atom[i] and string.char(atom[i]) or "" + end + + return buffer, table.concat(atom, "") +end + + +function decode(chunk) + local atom, buffer = {}, "" + + for i = 1, #chunk do + table.insert(atom, chunk:sub(i, i)) + + repeat + local shift = 3 + + if atom[1] == '=' then + if #atom < 3 then -- need more + break + elseif atom[2] == '\r' and atom[3] == '\n' then + -- eliminate soft line break + else + local char = unquote(atom[2], atom[3]) + if not char then + buffer = buffer .. table.concat(atom, "") + else + buffer = buffer .. char + end + end + + elseif atom[1] == '\r' then + if #atom < 2 then -- need more + break + elseif atom[2] == '\n' then + buffer, shift = buffer .. "\r\n", 2 + else -- neglect this '\r' and following char + shift = 2 + end + + else + if atom[1] == '\t' or printable(atom[1]) then + buffer, shift = buffer .. atom[1], 1 + end + end + + -- shift out used chars + for i = 1, 3 do atom[i] = atom[i + shift] end + + until #atom == 0 + end + + return buffer, table.concat(atom, "") +end + diff --git a/website/backend/app/libs/resty/smtp/tp.lua b/website/backend/app/libs/resty/smtp/tp.lua new file mode 100755 index 0000000..f9b62b5 --- /dev/null +++ b/website/backend/app/libs/resty/smtp/tp.lua @@ -0,0 +1,145 @@ +----------------------------------------------------------------------------- +-- Unified SMTP/FTP subsystem +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: tp.lua,v 1.22 2006/03/14 09:04:15 diego Exp $ +----------------------------------------------------------------------------- +-- Author: duhoobo +-- ChangeLog: +-- * 2014/04/06 03:47:15 - simplified for lua-module-module +----------------------------------------------------------------------------- + + +local base = _G +local string = require("string") + +local ltn12 = require("resty.smtp.ltn12") +local misc = require("resty.smtp.misc") + +module("resty.smtp.tp") + + +-- gets server reply (works for SMTP and FTP) +local function get_reply(c) + local code, current, sep + local line, err = c:receive("*l") + local reply = line + + if err then return nil, err end + + code, sep = misc.skip(2, string.find(line, "^(%d%d%d)(.?)")) + + if not code then return nil, "invalid server reply" end + if sep == "-" then -- reply is multiline + repeat + line, err = c:receive("*l") + + if err then return nil, err end + + current, sep = misc.skip(2, string.find(line, "^(%d%d%d)(.?)")) + reply = reply .. "\n" .. line + -- reply ends with same code + until code == current and sep == " " + end + + return code, reply +end + + +-- metatable for sock object +local metat = {__index= {}} + + +function metat.__index:expect(check) + local code, reply = get_reply(self.c) + + if not code then return nil, reply end + + if base.type(check) ~= "function" then + if base.type(check) == "table" then + for i, v in base.ipairs(check) do + if string.find(code, v) then + return base.tonumber(code), reply + end + end + + return nil, reply + + else -- string + if string.find(code, check) then + return base.tonumber(code), reply + else return nil, reply end + end + + else return check(base.tonumber(code), reply) end +end + + +function metat.__index:command(cmd, arg) + local request = cmd .. (arg and (" " .. arg) or "") .. "\r\n" + return self.c:send(request) +end + + +function metat.__index:sink(snk, pat) + local chunk, err = c:receive(pat) + return snk(chunk, err) +end + + +function metat.__index:send(data) + return self.c:send(data) +end + + +function metat.__index:receive(pat) + return self.c:receive(pat) +end + + +function metat.__index:source(source, step) + local sink = function(chunk, err) + if chunk then return self:send(chunk) + else return 1 end + end + + return ltn12.pump.all(source, sink, step or ltn12.pump.step) +end + + +-- closes the underlying c +function metat.__index:close() + self.c:close() + return 1 +end + + +-- connect with server and return c object +function connect(host, port, timeout, create, ssl) + local c, e = create() + if not c then return nil, e end + + c:settimeout(timeout) + + local r, e = c:connect(host, port) + if not r then + c:close() + return nil, e + end + + if ssl.enable then + if not c.sslhandshake then + c:close() + return nil, "socket does not have ssl support" + end + + local s, e = c:sslhandshake(nil, host, ssl.verify_cert) + if not s then + c:close() + return nil, "ssl handshake: " .. e + end + end + + return base.setmetatable({c= c}, metat) +end + diff --git a/website/backend/app/libs/utils.lua b/website/backend/app/libs/utils.lua new file mode 100755 index 0000000..120f89d --- /dev/null +++ b/website/backend/app/libs/utils.lua @@ -0,0 +1,153 @@ +local type = type +local pairs = pairs +local type = type +local mceil = math.ceil +local mfloor = math.floor +local mrandom = math.random +local mmodf = math.modf +local sgsub = string.gsub +local tinsert = table.insert +local date = require("app.libs.date") +local resty_sha256 = require "resty.sha256" +local str = require "resty.string" +local ngx_quote_sql_str = ngx.quote_sql_str + + + +local _M = {} + +function _M.encode(s) + local sha256 = resty_sha256:new() + sha256:update(s) + local digest = sha256:final() + return str.to_hex(digest) +end + + +function _M.clear_slash(s) + s, _ = sgsub(s, "(/+)", "/") + return s +end + +function _M.is_table_empty(t) + if t == nil or _G.next(t) == nil then + return true + else + return false + end +end + +function _M.table_is_array(t) + if type(t) ~= "table" then return false end + local i = 0 + for _ in pairs(t) do + i = i + 1 + if t[i] == nil then return false end + end + return true +end + +function _M.mixin(a, b) + if a and b then + for k, v in pairs(b) do + a[k] = b[k] + end + end + return a +end + +function _M.random() + return mrandom(0, 1000) +end + + +function _M.total_page(total_count, page_size) + local total_page = 0 + if total_count % page_size == 0 then + total_page = total_count / page_size + else + local tmp, _ = mmodf(total_count/page_size) + total_page = tmp + 1 + end + + return total_page +end + + +function _M.days_after_registry(req) + local diff = 0 + local diff_days = 0 -- default value, days after registry + + if req and req.session then + local user = req.session.get("user") + local create_time = user and user.create_time + if create_time then + local now = date() -- seconds + create_time = date(create_time) + diff = date.diff(now, create_time):spandays() + diff_days = mfloor(diff) + end + end + + return diff_days, diff +end + +function _M.now() + local n = date() + local result = n:fmt("%Y-%m-%d %H:%M:%S") + return result +end + +function _M.secure_str(str) + return ngx_quote_sql_str(str) +end + + +function _M.string_split(str, delimiter) + if str==nil or str=='' or delimiter==nil then + return nil + end + + local result = {} + for match in (str..delimiter):gmatch("(.-)"..delimiter) do + tinsert(result, match) + end + return result +end + +return _M + + + + +-- local resty_sha256 = require "resty.sha256" + -- local str = require "resty.string" + -- local sha256 = resty_sha256:new() + -- ngx.say(sha256:update("hello")) + -- local digest = sha256:final() + -- ngx.say("sha256: ", str.to_hex(digest)) + + -- local resty_md5 = require "resty.md5" + -- local md5 = resty_md5:new() + -- if not md5 then + -- ngx.say("failed to create md5 object") + -- return + -- end + + -- local ok = md5:update("hel") + -- if not ok then + -- ngx.say("failed to add data") + -- return + -- end + + -- ok = md5:update("lo") + -- if not ok then + -- ngx.say("failed to add data") + -- return + -- end + + -- local digest = md5:final() + + -- local str = require "resty.string" + -- ngx.say("md5: ", str.to_hex(digest)) + -- -- yield "md5: 5d41402abc4b2a76b9719d911017c592" diff --git a/website/backend/app/libs/uuid.lua b/website/backend/app/libs/uuid.lua new file mode 100755 index 0000000..e65ddb8 --- /dev/null +++ b/website/backend/app/libs/uuid.lua @@ -0,0 +1,205 @@ +--------------------------------------------------------------------------------------- +-- Copyright 2012 Rackspace (original), 2013 Thijs Schreijer (modifications) +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS-IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- see http://www.ietf.org/rfc/rfc4122.txt +-- +-- Note that this is not a true version 4 (random) UUID. Since `os.time()` precision is only 1 second, it would be hard +-- to guarantee spacial uniqueness when two hosts generate a uuid after being seeded during the same second. This +-- is solved by using the node field from a version 1 UUID. It represents the mac address. +-- +-- 28-apr-2013 modified by Thijs Schreijer from the original [Rackspace code](https://github.com/kans/zirgo/blob/807250b1af6725bad4776c931c89a784c1e34db2/util/uuid.lua) as a generic Lua module. +-- Regarding the above mention on `os.time()`; the modifications use the `socket.gettime()` function from LuaSocket +-- if available and hence reduce that problem (provided LuaSocket has been loaded before uuid). +-- +-- **6-nov-2015 Please take note of this issue**; [https://github.com/Mashape/kong/issues/478](https://github.com/Mashape/kong/issues/478) +-- It demonstrates the problem of using time as a random seed. Specifically when used from multiple processes. +-- So make sure to seed only once, application wide. And to not have multiple processes do that +-- simultaneously (like nginx does for example). + +local M = {} +local math = require('math') +local os = require('os') +local string = require('string') + +local bitsize = 32 -- bitsize assumed for Lua VM. See randomseed function below. +local lua_version = tonumber(_VERSION:match("%d%.*%d*")) -- grab Lua version used + +local MATRIX_AND = {{0,0},{0,1} } +local MATRIX_OR = {{0,1},{1,1}} +local HEXES = '0123456789abcdef' + +local math_floor = math.floor +local math_random = math.random +local math_abs = math.abs +local string_sub = string.sub +local to_number = tonumber +local assert = assert +local type = type + +-- performs the bitwise operation specified by truth matrix on two numbers. +local function BITWISE(x, y, matrix) + local z = 0 + local pow = 1 + while x > 0 or y > 0 do + z = z + (matrix[x%2+1][y%2+1] * pow) + pow = pow * 2 + x = math_floor(x/2) + y = math_floor(y/2) + end + return z +end + +local function INT2HEX(x) + local s,base = '',16 + local d + while x > 0 do + d = x % base + 1 + x = math_floor(x/base) + s = string_sub(HEXES, d, d)..s + end + while #s < 2 do s = "0" .. s end + return s +end + +---------------------------------------------------------------------------- +-- Creates a new uuid. Either provide a unique hex string, or make sure the +-- random seed is properly set. The module table itself is a shortcut to this +-- function, so `my_uuid = uuid.new()` equals `my_uuid = uuid()`. +-- +-- For proper use there are 3 options; +-- +-- 1. first require `luasocket`, then call `uuid.seed()`, and request a uuid using no +-- parameter, eg. `my_uuid = uuid()` +-- 2. use `uuid` without `luasocket`, set a random seed using `uuid.randomseed(some_good_seed)`, +-- and request a uuid using no parameter, eg. `my_uuid = uuid()` +-- 3. use `uuid` without `luasocket`, and request a uuid using an unique hex string, +-- eg. `my_uuid = uuid(my_networkcard_macaddress)` +-- +-- @return a properly formatted uuid string +-- @param hwaddr (optional) string containing a unique hex value (e.g.: `00:0c:29:69:41:c6`), to be used to compensate for the lesser `math_random()` function. Use a mac address for solid results. If omitted, a fully randomized uuid will be generated, but then you must ensure that the random seed is set properly! +-- @usage +-- local uuid = require("uuid") +-- print("here's a new uuid: ",uuid()) +function M.new(hwaddr) + -- bytes are treated as 8bit unsigned bytes. + local bytes = { + math_random(0, 255), + math_random(0, 255), + math_random(0, 255), + math_random(0, 255), + math_random(0, 255), + math_random(0, 255), + math_random(0, 255), + math_random(0, 255), + math_random(0, 255), + math_random(0, 255), + math_random(0, 255), + math_random(0, 255), + math_random(0, 255), + math_random(0, 255), + math_random(0, 255), + math_random(0, 255) + } + + if hwaddr then + assert(type(hwaddr)=="string", "Expected hex string, got "..type(hwaddr)) + -- Cleanup provided string, assume mac address, so start from back and cleanup until we've got 12 characters + local i,str = #hwaddr, hwaddr + hwaddr = "" + while i>0 and #hwaddr<12 do + local c = str:sub(i,i):lower() + if HEXES:find(c, 1, true) then + -- valid HEX character, so append it + hwaddr = c..hwaddr + end + i = i - 1 + end + assert(#hwaddr == 12, "Provided string did not contain at least 12 hex characters, retrieved '"..hwaddr.."' from '"..str.."'") + + -- no split() in lua. :( + bytes[11] = to_number(hwaddr:sub(1, 2), 16) + bytes[12] = to_number(hwaddr:sub(3, 4), 16) + bytes[13] = to_number(hwaddr:sub(5, 6), 16) + bytes[14] = to_number(hwaddr:sub(7, 8), 16) + bytes[15] = to_number(hwaddr:sub(9, 10), 16) + bytes[16] = to_number(hwaddr:sub(11, 12), 16) + end + + -- set the version + bytes[7] = BITWISE(bytes[7], 0x0f, MATRIX_AND) + bytes[7] = BITWISE(bytes[7], 0x40, MATRIX_OR) + -- set the variant + bytes[9] = BITWISE(bytes[7], 0x3f, MATRIX_AND) + bytes[9] = BITWISE(bytes[7], 0x80, MATRIX_OR) + return INT2HEX(bytes[1])..INT2HEX(bytes[2])..INT2HEX(bytes[3])..INT2HEX(bytes[4]).."-".. + INT2HEX(bytes[5])..INT2HEX(bytes[6]).."-".. + INT2HEX(bytes[7])..INT2HEX(bytes[8]).."-".. + INT2HEX(bytes[9])..INT2HEX(bytes[10]).."-".. + INT2HEX(bytes[11])..INT2HEX(bytes[12])..INT2HEX(bytes[13])..INT2HEX(bytes[14])..INT2HEX(bytes[15])..INT2HEX(bytes[16]) +end + +---------------------------------------------------------------------------- +-- Improved randomseed function. +-- Lua 5.1 and 5.2 both truncate the seed given if it exceeds the integer +-- range. If this happens, the seed will be 0 or 1 and all randomness will +-- be gone (each application run will generate the same sequence of random +-- numbers in that case). This improved version drops the most significant +-- bits in those cases to get the seed within the proper range again. +-- @param seed the random seed to set (integer from 0 - 2^32, negative values will be made positive) +-- @return the (potentially modified) seed used +-- @usage +-- local socket = require("socket") -- gettime() has higher precision than os.time() +-- local uuid = require("uuid") +-- -- see also example at uuid.seed() +-- uuid.randomseed(socket.gettime()*10000) +-- print("here's a new uuid: ",uuid()) +function M.randomseed(seed) + seed = math_floor(math_abs(seed)) + if seed >= (2^bitsize) then + -- integer overflow, so reduce to prevent a bad seed + seed = seed - math_floor(seed / 2^bitsize) * (2^bitsize) + end + if lua_version < 5.2 then + -- 5.1 uses (incorrect) signed int + math.randomseed(seed - 2^(bitsize-1)) + else + -- 5.2 uses (correct) unsigned int + math.randomseed(seed) + end + return seed +end + +---------------------------------------------------------------------------- +-- Seeds the random generator. +-- It does so in 2 possible ways; +-- +-- 1. use `os.time()`: this only offers resolution to one second (used when +-- LuaSocket hasn't been loaded yet +-- 2. use luasocket `gettime()` function, but it only does so when LuaSocket +-- has been required already. +-- @usage +-- local socket = require("socket") -- gettime() has higher precision than os.time() +-- -- LuaSocket loaded, so below line does the same as the example from randomseed() +-- uuid.seed() +-- print("here's a new uuid: ",uuid()) +function M.seed() + if package.loaded["socket"] and package.loaded["socket"].gettime then + return M.randomseed(package.loaded["socket"].gettime()*10000) + else + return M.randomseed(os.time()) + end +end + +return setmetatable( M, { __call = function(self, hwaddr) return self.new(hwaddr) end} ) diff --git a/website/backend/app/main.lua b/website/backend/app/main.lua new file mode 100755 index 0000000..4285944 --- /dev/null +++ b/website/backend/app/main.lua @@ -0,0 +1,6 @@ +inspect = require("app.libs.inspect") +require("app.config.error_code") +require "app.libs.log_api" + +local app = require("app.server") +app:run() diff --git a/website/backend/app/middleware/README.md b/website/backend/app/middleware/README.md new file mode 100644 index 0000000..0dd3f92 --- /dev/null +++ b/website/backend/app/middleware/README.md @@ -0,0 +1,19 @@ + +### 自定义插件目录(define your own middleware) + + +You are recommended to define your own middlewares and keep them in one place to manage. + +建议用户将自定义插件存放在此目录下统一管理,然后在其他地方引用,插件的格式如下: + +``` +local middleware = function(params) + return function(req, res, next) + -- do something with req/res + next() + end +end + +return middleware +``` + diff --git a/website/backend/app/middleware/check_login.lua b/website/backend/app/middleware/check_login.lua new file mode 100755 index 0000000..529887b --- /dev/null +++ b/website/backend/app/middleware/check_login.lua @@ -0,0 +1,60 @@ +local smatch = string.match +local sfind = string.find + +local function is_login(req) + local user + if req.session then + user = req.session.get("user") + if user and user.username and user.userid then + return true, user + end + end + return false, nil +end + +local function check_login(whitelist) + return function(req, res, next) + local requestPath = req.path + local in_white_list = false + if requestPath == "/" then + in_white_list = true + else + for i, v in ipairs(whitelist) do + local match, err = smatch(requestPath, v) + if match then + in_white_list = true + end + end + end + + local islogin, user= is_login(req) + + if in_white_list then + res.locals.login = islogin + res.locals.username = user and user.username + res.locals.userid = user and user.userid + res.locals.create_time = user and user.create_time + next() + else + if islogin then + res.locals.login = true + res.locals.username = user.username + res.locals.userid = user.userid + res.locals.create_time = user.create_time + next() + else + if sfind(req.headers["Accept"], "application/json") then + res:json({ + code = AUTH_ERROR.account_login, + message = system_error_msg(AUTH_ERROR.account_login), + }) + -- else + -- res:redirect("/api/login") + end + end + end + end +end + +return check_login + diff --git a/website/backend/app/middleware/inject_app_info.lua b/website/backend/app/middleware/inject_app_info.lua new file mode 100755 index 0000000..74c1457 --- /dev/null +++ b/website/backend/app/middleware/inject_app_info.lua @@ -0,0 +1,11 @@ +--- 中间件示例: 为每个请求注入一些通用的变量 +local lor = require("lor.index") +return function() + return function(req, res, next) + -- res.locals是一个table, 可以在这里注入一些“全局”变量 + -- 这个示例里注入app的名称和版本号, 在渲染页面时即可使用 + res.locals.app_name = "lor application" + res.locals.app_version = lor.version or "" + next() + end +end diff --git a/website/backend/app/middleware/uploader.lua b/website/backend/app/middleware/uploader.lua new file mode 100755 index 0000000..09bb0f6 --- /dev/null +++ b/website/backend/app/middleware/uploader.lua @@ -0,0 +1,139 @@ +local upload = require("resty.upload") +local uuid = require("app.libs.uuid") + +local sfind = string.find +local match = string.match +local ngx_var = ngx.var + + + +local function getextension(filename) + return filename:match(".+%.(%w+)$") +end + + +local function _multipart_formdata(config) + + local form, err = upload:new(config.chunk_size) + if not form then + ngx.log(ngx.ERR, "failed to new upload: ", err) + ngx.exit(500) + end + form:set_timeout(config.recieve_timeout) + + + local unique_name = uuid() + local success, msg = false, "" + local file, origin_filename, filename, path, extname, err + while true do + local typ, res, err = form:read() + + if not typ then + success = false + msg = "failed to read" + ngx.log(ngx.ERR, "failed to read: ", err) + return success, msg + end + + if typ == "header" then + if res[1] == "Content-Disposition" then + key = match(res[2], "name=\"(.-)\"") + origin_filename = match(res[2], "filename=\"(.-)\"") + elseif res[1] == "Content-Type" then + filetype = res[2] + end + + if origin_filename and filetype then + if not extname then + extname = getextension(origin_filename) + end + + if extname ~= "png" and extname ~= "jpg" and extname ~= "jpeg" and extname ~= "bmp" and extname ~= "gif" then + success = false + msg = "not allowed upload file type" + ngx.log(ngx.ERR, "not allowed upload file type:", origin_filename) + return success, msg + end + + filename = unique_name .. "." .. extname + path = config.dir.. "/" .. filename + + + file, err = io.open(path, "w+") + + if err then + success = false + msg = "open file error" + ngx.log(ngx.ERR, "open file error:", err) + return success, msg + end + end + + elseif typ == "body" then + if file then + file:write(res) + success = true + else + success = false + msg = "upload file error" + ngx.log(ngx.ERR, "upload file error, path:", path) + return success, msg + end + elseif typ == "part_end" then + file:close() + file = nil + elseif typ == "eof" then + break + else + -- do nothing + end + end + + return success, msg, origin_filename, extname, path, filename +end + + + +local function uploader(config) + return function(req, res, next) + + if ngx_var.request_method == "POST" then + local get_headers = ngx.req.get_headers() + local header = get_headers['Content-Type'] + if header then + local is_multipart = sfind(header, "multipart") + if is_multipart and is_multipart>0 then + config = config or {} + config.dir = config.dir or "/tmp" + config.chunk_size = config.chunk_size or 4096 + config.recieve_timeout = config.recieve_timeout or 20000 -- 20s + + local success, msg, origin_filename, extname, path, filename = _multipart_formdata(config) + if success then + req.file = req.file or {} + req.file.success = true + req.file.origin_filename = origin_filename + req.file.extname = extname + req.file.path = path + req.file.filename = filename + else + req.file = req.file or {} + req.file.success = false + req.file.msg = msg + end + next() + + else + next() + end + else + next() + end + else + next() + end + end +end + +return uploader + diff --git a/website/backend/app/model/user.lua b/website/backend/app/model/user.lua new file mode 100755 index 0000000..ef40634 --- /dev/null +++ b/website/backend/app/model/user.lua @@ -0,0 +1,72 @@ +local DB = require("app.libs.db") +local db = DB:new() + +local user_model = {} + + +function user_model:new(username, password, avatar, rolelv) + return db:query("insert into user(username, password, avatar, role_lv) values(?,?,?,?)", + {username, password, avatar, rolelv}) +end + +function user_model:query(username, password) + local res, err = db:query("select * from user where username=? and password=?", {username, password}) + return res, err +end + +-- function user_model:query_by_id(id) +-- local result, err = db:query("select * from user where id=?", {tonumber(id)}) +-- if not result or err or type(result) ~= "table" or #result ~=1 then +-- return nil, err +-- else +-- return result[1], err +-- end +-- end + +-- return user, err +function user_model:query_by_username(username) + local res, err = db:query("select * from user where username=? limit 1", {username}) + if not res or err or type(res) ~= "table" or #res ~=1 then + return nil, err or "error" + end + + return res[1], err +end + +function user_model:update_avatar(userid, avatar) + db:query("update user set avatar=? where id=?", {avatar, userid}) +end + +function user_model:update_pwd(userid, pwd) + local res, err = db:query("update user set password=? where id=?", {pwd, userid}) + if not res or err then + return false + else + return true + end + +end + +function user_model:update(userid, email, email_public, city, company, github, website, sign) + local res, err = db:query("update user set email=?, email_public=?, city=?, company=?, github=?, website=?, sign=? where id=?", + { email, email_public, city, company, github, website, sign, userid}) + + if not res or err then + return false + else + return true + end +end + +function user_model:get_total_count() + local res, err = db:query("select count(id) as c from user") + + if err or not res or #res~=1 or not res[1].c then + return 0 + else + return res[1].c + end +end + + +return user_model diff --git a/website/backend/app/router.lua b/website/backend/app/router.lua new file mode 100755 index 0000000..c5fac6d --- /dev/null +++ b/website/backend/app/router.lua @@ -0,0 +1,7 @@ +-- 业务路由管理 +local apiRouter = require("app.routes.api") + +return function(app) + app:use("/api", apiRouter) +end + diff --git a/website/backend/app/routes/api.lua b/website/backend/app/routes/api.lua new file mode 100755 index 0000000..631f717 --- /dev/null +++ b/website/backend/app/routes/api.lua @@ -0,0 +1,48 @@ +local lor = require("lor.index") +local apiRouter = lor:Router() -- 生成一个group router对象 + + +local user_login = require("app.controller.user.login") +local user_signup = require("app.controller.user.signup") +local user_info = require("app.controller.user.user_info") + +--注册 +apiRouter:post("/signup", function(req, res, next) + local body = req.body + local username = body.username + local password = body.password + + local msg = user_signup(username, password) + return res:json(msg) +end) + +--登录 +apiRouter:post("/login", function(req, res, next) + local body = req.body + local username = body.username + local password = body.password + + local msg = user_login(req, username, password) + return res:json(msg) +end) + +apiRouter:get("/user", function(req, res, next) + local msg = user_info(req, res) + return res:json(msg) +end) + +apiRouter:get("/list", function(req, res, next) + local msg = user_info(req, res) + return res:json(msg) +end) + + +apiRouter:post("/logout", function(req, res, next) + res.locals.login = false + res.locals.username = "" + res.locals.userid = 0 + res.locals.create_time = "" + req.session.destroy() +end) + +return apiRouter \ No newline at end of file diff --git a/website/backend/app/server.lua b/website/backend/app/server.lua new file mode 100755 index 0000000..1cc565b --- /dev/null +++ b/website/backend/app/server.lua @@ -0,0 +1,90 @@ +local string_find = string.find +local string_lower = string.lower + +local lor = require("lor.index") +local router = require("app.router") +local app = lor() + +local cors_header = require "cors_header" + +local config = require("app.config.config") +local whitelist = config.whitelist +local view_config = config.view_config +local upload_config = config.upload_config + +-- 模板配置 +app:conf("view enable", true) +app:conf("view engine", view_config.engine) +app:conf("view ext", view_config.ext) +app:conf("views", view_config.views) + + +-- session和cookie支持,如果不需要可注释以下配置 +local mw_cookie = require("lor.lib.middleware.cookie") +local mw_session = require("lor.lib.middleware.session") +local mw_check_login = require("app.middleware.check_login") +-- local mw_uploader = require("app.middleware.uploader") +-- 自定义中间件1: 注入一些全局变量供模板渲染使用 +local mw_inject_version = require("app.middleware.inject_app_info") + +app:use(mw_cookie()) +app:use(mw_session({ + session_key = "__rilladmin_app__", -- the key injected in cookie + session_aes_key = "aes_&%$#@(*&Gjjd563hdngds35781fhxgh", -- should set by yourself + timeout = 3600 -- default session timeout is 3600 seconds +})) + +app:use(mw_inject_version()) + +-- intercepter: login or not +app:use(mw_check_login(whitelist)) + +-- -- uploader +-- app:use(mw_uploader({ +-- dir = upload_config.dir +-- })) + + +--自定义中间件2: 设置响应头 +app:use(function(req, res, next) + cors_header(res) + next() +end) + +router(app) -- 业务路由处理 + +-- 错误处理插件,可根据需要定义多个 +app:erroruse(function(err, req, res, next) + -- ERROR("error: ", err) + + cors_header(res) + + local method = req.method and string_lower(req.method) + if method == "options" then + res:set_header("Access-Control-Max-Age", "1728000") + res:set_header("Content-Length", "0") + return + end + + if req:is_found() ~= true then + if string_find(req.headers["Accept"], "application/json") then + res:status(404):json({ + success = false, + msg = "404! sorry, not found." + }) + else + res:status(404):send("404! sorry, not found. " .. (req.path or "")) + end + else + if string_find(req.headers["Accept"], "application/json") then + res:status(500):json({ + success = false, + msg = "500! internal error, please check the log." + }) + else + res:status(500):send("internal error, please check the log.") + end + end +end) + +return app diff --git a/website/backend/app/static/README.md b/website/backend/app/static/README.md new file mode 100755 index 0000000..28889bf --- /dev/null +++ b/website/backend/app/static/README.md @@ -0,0 +1,11 @@ + +### 静态文件目录(static files directory) + +nginx对应配置为 + +``` +location /static { + alias app/static; +} +``` + diff --git a/website/backend/app/views/.gitkeep b/website/backend/app/views/.gitkeep new file mode 100755 index 0000000..e69de29 diff --git a/website/backend/conf/README.md b/website/backend/conf/README.md new file mode 100755 index 0000000..e7f122c --- /dev/null +++ b/website/backend/conf/README.md @@ -0,0 +1,5 @@ + +### nginx configuration directory +## +/usr/local/openresty/nginx/sbin/nginx -c /usr/local/openresty/nginx/conf/nginx.conf + diff --git a/website/backend/conf/mime.types b/website/backend/conf/mime.types new file mode 100644 index 0000000..cd3d700 --- /dev/null +++ b/website/backend/conf/mime.types @@ -0,0 +1,88 @@ +types { + text/html html htm shtml; + text/css css; + text/xml xml; + image/gif gif; + image/jpeg jpeg jpg; + application/javascript js; + application/atom+xml atom; + application/rss+xml rss; + + text/mathml mml; + text/plain txt; + text/vnd.sun.j2me.app-descriptor jad; + text/vnd.wap.wml wml; + text/x-component htc; + + image/png png; + image/tiff tif tiff; + image/vnd.wap.wbmp wbmp; + image/x-icon ico; + image/x-jng jng; + image/x-ms-bmp bmp; + image/svg+xml svg svgz; + image/webp webp; + + application/font-woff woff; + application/java-archive jar war ear; + application/json json; + application/mac-binhex40 hqx; + application/msword doc; + application/pdf pdf; + application/postscript ps eps ai; + application/rtf rtf; + application/vnd.apple.mpegurl m3u8; + application/vnd.ms-excel xls; + application/vnd.ms-fontobject eot; + application/vnd.ms-powerpoint ppt; + application/vnd.wap.wmlc wmlc; + application/vnd.google-earth.kml+xml kml; + application/vnd.google-earth.kmz kmz; + application/x-7z-compressed 7z; + application/x-cocoa cco; + application/x-java-archive-diff jardiff; + application/x-java-jnlp-file jnlp; + application/x-makeself run; + application/x-perl pl pm; + application/x-pilot prc pdb; + application/x-rar-compressed rar; + application/x-redhat-package-manager rpm; + application/x-sea sea; + application/x-shockwave-flash swf; + application/x-stuffit sit; + application/x-tcl tcl tk; + application/x-x509-ca-cert der pem crt; + application/x-xpinstall xpi; + application/xhtml+xml xhtml; + application/xspf+xml xspf; + application/zip zip; + + application/octet-stream bin exe dll; + application/octet-stream deb; + application/octet-stream dmg; + application/octet-stream iso img; + application/octet-stream msi msp msm; + + application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; + application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; + + audio/midi mid midi kar; + audio/mpeg mp3; + audio/ogg ogg; + audio/x-m4a m4a; + audio/x-realaudio ra; + + video/3gpp 3gpp 3gp; + video/mp2t ts; + video/mp4 mp4; + video/mpeg mpeg mpg; + video/quicktime mov; + video/webm webm; + video/x-flv flv; + video/x-m4v m4v; + video/x-mng mng; + video/x-ms-asf asx asf; + video/x-ms-wmv wmv; + video/x-msvideo avi; +} diff --git a/website/backend/conf/nginx-dev.conf b/website/backend/conf/nginx-dev.conf new file mode 100755 index 0000000..780c25c --- /dev/null +++ b/website/backend/conf/nginx-dev.conf @@ -0,0 +1,98 @@ + +pid tmp/nginx.pid; +worker_processes 4; +#nginx权限问题 https://github.com/smallnewer/bugs/issues/64 ps aux|grep nginx|grep -v grep +user root; + +events { + worker_connections 4096; +} + +http { + include ./mime.types; + + client_max_body_size 10m; #允许客户端请求的最大单文件字节数 + client_body_buffer_size 10m; #缓冲区代理缓冲用户端请求的最大字节数 + + sendfile on; + keepalive_timeout 65; + charset utf8; + + lua_package_path "./app/?.lua;./app/libs/?.lua;./?.lua;./lor/?.lua;./lor/lib/?.lua;;;;"; +# lua_package_path "./app/?.lua;./app/library/?.lua;./app/?/init.lua;./?.lua;/usr/local/lor/?.lua;/usr/local/lor/?/init.lua;;"; + lua_package_cpath "./app/librarys/?.so;/usr/local/lor/?.so;;"; + lua_code_cache on; # set on @production + #LUA_SHARED_DICT + + server { + listen 9517; + #server_name localhost; + + # Access log with buffer, or disable it completetely if unneeded + access_log logs/dev-www-access.log combined buffer=16k; + # Error log + error_log logs/dev-www-error.log; + + location /admin { + root ./www/; + + # try_files $uri $uri/ ; + index index.html index.htm; + } + } + + server { + listen 9518; + + # Access log with buffer, or disable it completetely if unneeded + access_log logs/dev-www-access.log combined buffer=16k; + # Error log + error_log logs/dev-www-error.log; + + location /api/profiler { + proxy_pass http://127.0.0.1:9527; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + + proxy_set_header Host $host:$server_port; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + location /api/ { + proxy_pass http://127.0.0.1:9527; + } + } + + server { + listen 7777; + #server_name localhost; + set $template_root ''; + + # # 项目本身的静态文件 + # location /static/css { + # alias ./app/static/css; + # } + # location /static/img { + # alias ./app/static/img; + # } + # location /static/fonts { + # alias ./app/static/fonts; + # } + # location /static/js { + # alias ./app/static/js; + # } + + + # Access log with buffer, or disable it completetely if unneeded + access_log logs/dev-api-access.log combined buffer=16k; + # Error log + error_log logs/dev-api-error.log; + + # lor runtime + location / { + content_by_lua_file ./app/main.lua; + } + } +} \ No newline at end of file diff --git a/website/backend/lor/.gitignore b/website/backend/lor/.gitignore new file mode 100755 index 0000000..fa3abaf --- /dev/null +++ b/website/backend/lor/.gitignore @@ -0,0 +1,12 @@ +###test +local_install.sh +gen_rockspec.sh +test.sh +test.lua +snippets.lua +.idea +*.iml +*.rock +*.rockspec +lor-* +.DS_Store diff --git a/website/backend/lor/.luacheckrc b/website/backend/lor/.luacheckrc new file mode 100755 index 0000000..d8bfebd --- /dev/null +++ b/website/backend/lor/.luacheckrc @@ -0,0 +1,4 @@ +std = "ngx_lua" +globals = {"LOR_FRAMEWORK_DEBUG"} + +exclude_files = {"test/*", "resty", "bin"} diff --git a/website/backend/lor/.sublimelinterrc b/website/backend/lor/.sublimelinterrc new file mode 100755 index 0000000..0a0cc96 --- /dev/null +++ b/website/backend/lor/.sublimelinterrc @@ -0,0 +1,16 @@ +{ + "linters": { + "luacheck": { + "@disable": false, + "args": [], + "std": "ngx_lua", + "excludes": ["test/*", "resty", "bin", "*_spec.lua", "*.test.lua"], + "globals": ["LOR_FRAMEWORK_DEBUG"], + "ignore": [ + "channel" + ], + "limit": null, + "only": [] + } + } +} diff --git a/website/backend/lor/.travis.yml b/website/backend/lor/.travis.yml new file mode 100755 index 0000000..c724fc6 --- /dev/null +++ b/website/backend/lor/.travis.yml @@ -0,0 +1,42 @@ +language: c + +sudo: false + +env: + global: + - LUAROCKS=2.2.2 + matrix: + # Lua 5.1 + - LUA=lua5.1 + # LuaJIT latest stable version (2.0.4) + - LUA=luajit + # Openresty + LuaJIT + mysql + - LUA=luajit SERVER=openresty + # - LUA=luajit2.0 # current head of 2.0 branch + # - LUA=luajit2.1 # current head of 2.1 branch + # Not supported + # - LUA=lua5.2 + # - LUA=lua5.3 + +branches: + - master + - v0.3.0 + + +before_install: + - source .travis/setenv_lua.sh + +install: + - luarocks install https://luarocks.org/manifests/olivine-labs/busted-2.0.rc12-1.rockspec + - luarocks install lrexlib-pcre 2.7.2-1 + - luarocks install luaposix + - luarocks install lua-cjson + #- luarocks make + +script: + - busted spec/* + +notifications: + email: + on_success: change + on_failure: always diff --git a/website/backend/lor/.travis/platform.sh b/website/backend/lor/.travis/platform.sh new file mode 100755 index 0000000..7259a7d --- /dev/null +++ b/website/backend/lor/.travis/platform.sh @@ -0,0 +1,15 @@ +if [ -z "${PLATFORM:-}" ]; then + PLATFORM=$TRAVIS_OS_NAME; +fi + +if [ "$PLATFORM" == "osx" ]; then + PLATFORM="macosx"; +fi + +if [ -z "$PLATFORM" ]; then + if [ "$(uname)" == "Linux" ]; then + PLATFORM="linux"; + else + PLATFORM="macosx"; + fi; +fi diff --git a/website/backend/lor/.travis/setenv_lua.sh b/website/backend/lor/.travis/setenv_lua.sh new file mode 100755 index 0000000..942fa14 --- /dev/null +++ b/website/backend/lor/.travis/setenv_lua.sh @@ -0,0 +1,9 @@ +export PATH=${PATH}:$HOME/.lua:$HOME/.local/bin:${TRAVIS_BUILD_DIR}/install/luarocks/bin +export PATH=${PATH}:$HOME/.lua:$HOME/.local/bin:${TRAVIS_BUILD_DIR}/install/openresty/nginx/sbin +export PATH=${PATH}:$HOME/.lua:$HOME/.local/bin:${TRAVIS_BUILD_DIR}/install/openresty/bin +bash .travis/setup_lua.sh +if [ "$SERVER" == "openresty" ]; then + bash .travis/setup_servers.sh +fi + +eval `$HOME/.lua/luarocks path` diff --git a/website/backend/lor/.travis/setup_lua.sh b/website/backend/lor/.travis/setup_lua.sh new file mode 100755 index 0000000..e434ddc --- /dev/null +++ b/website/backend/lor/.travis/setup_lua.sh @@ -0,0 +1,109 @@ +#! /bin/bash + +# A script for setting up environment for travis-ci testing. +# Sets up Lua and Luarocks. +# LUA must be "lua5.1", "lua5.2" or "luajit". +# luajit2.0 - master v2.0 +# luajit2.1 - master v2.1 + +set -eufo pipefail + +LUAJIT_BASE="LuaJIT-2.0.4" + +source .travis/platform.sh + +LUA_HOME_DIR=$TRAVIS_BUILD_DIR/install/lua + +LR_HOME_DIR=$TRAVIS_BUILD_DIR/install/luarocks + +mkdir $HOME/.lua + +LUAJIT="no" + +if [ "$PLATFORM" == "macosx" ]; then + if [ "$LUA" == "luajit" ]; then + LUAJIT="yes"; + fi + if [ "$LUA" == "luajit2.0" ]; then + LUAJIT="yes"; + fi + if [ "$LUA" == "luajit2.1" ]; then + LUAJIT="yes"; + fi; +elif [ "$(expr substr $LUA 1 6)" == "luajit" ]; then + LUAJIT="yes"; +fi + +mkdir -p "$LUA_HOME_DIR" + +if [ "$LUAJIT" == "yes" ]; then + + git clone https://github.com/LuaJIT/LuaJIT $LUAJIT_BASE; + + cd $LUAJIT_BASE + + if [ "$LUA" == "luajit2.1" ]; then + git checkout v2.1; + # force the INSTALL_TNAME to be luajit + perl -i -pe 's/INSTALL_TNAME=.+/INSTALL_TNAME= luajit/' Makefile + else + git checkout v2.0.4; + fi + + make && make install PREFIX="$LUA_HOME_DIR" + + ln -s $LUA_HOME_DIR/bin/luajit $HOME/.lua/luajit + ln -s $LUA_HOME_DIR/bin/luajit $HOME/.lua/lua; + +else + + if [ "$LUA" == "lua5.1" ]; then + curl http://www.lua.org/ftp/lua-5.1.5.tar.gz | tar xz + cd lua-5.1.5; + fi + + # Build Lua without backwards compatibility for testing + perl -i -pe 's/-DLUA_COMPAT_(ALL|5_2)//' src/Makefile + make $PLATFORM + make INSTALL_TOP="$LUA_HOME_DIR" install; + + ln -s $LUA_HOME_DIR/bin/lua $HOME/.lua/lua + ln -s $LUA_HOME_DIR/bin/luac $HOME/.lua/luac; + +fi + +cd $TRAVIS_BUILD_DIR + +lua -v + +LUAROCKS_BASE=luarocks-$LUAROCKS + +curl --location http://luarocks.org/releases/$LUAROCKS_BASE.tar.gz | tar xz + +cd $LUAROCKS_BASE + +if [ "$LUA" == "luajit" ]; then + ./configure --lua-suffix=jit --with-lua-include="$LUA_HOME_DIR/include/luajit-2.0" --prefix="$LR_HOME_DIR"; +elif [ "$LUA" == "luajit2.0" ]; then + ./configure --lua-suffix=jit --with-lua-include="$LUA_HOME_DIR/include/luajit-2.0" --prefix="$LR_HOME_DIR"; +elif [ "$LUA" == "luajit2.1" ]; then + ./configure --lua-suffix=jit --with-lua-include="$LUA_HOME_DIR/include/luajit-2.1" --prefix="$LR_HOME_DIR"; +else + ./configure --with-lua="$LUA_HOME_DIR" --prefix="$LR_HOME_DIR" +fi + +make build && make install + +ln -s $LR_HOME_DIR/bin/luarocks $HOME/.lua/luarocks + +cd $TRAVIS_BUILD_DIR + +luarocks --version + +rm -rf $LUAROCKS_BASE + +if [ "$LUAJIT" == "yes" ]; then + rm -rf $LUAJIT_BASE; +elif [ "$LUA" == "lua5.1" ]; then + rm -rf lua-5.1.5; +fi diff --git a/website/backend/lor/.travis/setup_servers.sh b/website/backend/lor/.travis/setup_servers.sh new file mode 100755 index 0000000..3b9612c --- /dev/null +++ b/website/backend/lor/.travis/setup_servers.sh @@ -0,0 +1,33 @@ +#! /bin/bash + +# A script for setting up environment for travis-ci testing. +# Sets up openresty. +OPENRESTY_VERSION="1.9.3.1" +OPENRESTY_DIR=$TRAVIS_BUILD_DIR/install/openresty + +#if [ "$LUA" == "lua5.1" ]; then +# luarocks install LuaBitOp +#fi + +wget https://openresty.org/download/ngx_openresty-$OPENRESTY_VERSION.tar.gz +tar xzvf ngx_openresty-$OPENRESTY_VERSION.tar.gz +cd ngx_openresty-$OPENRESTY_VERSION/ + +./configure --prefix="$OPENRESTY_DIR" --with-luajit + +make +make install + +ln -s $OPENRESTY_DIR/bin/resty $HOME/.lua/resty +ln -s $OPENRESTY_DIR/nginx/sbin/nginx $HOME/.lua/nginx + +export PATH=${PATH}:$HOME/.lua:$HOME/.local/bin:${TRAVIS_BUILD_DIR}/install/openresty/nginx/sbin +export PATH=${PATH}:$HOME/.lua:$HOME/.local/bin:${TRAVIS_BUILD_DIR}/install/openresty/bin + +nginx -v +resty -V + +cd ../ +rm -rf ngx_openresty-$OPENRESTY_VERSION +cd $TRAVIS_BUILD_DIR + diff --git a/website/backend/lor/Changes.md b/website/backend/lor/Changes.md new file mode 100755 index 0000000..97fdd6f --- /dev/null +++ b/website/backend/lor/Changes.md @@ -0,0 +1,174 @@ +### v0.3.4 2017.08.30 + +- 修复默认session插件的`session_aes_secret`长度问题 + - 此问题存在于OpenResty v1.11.2.5版本及可能之后的版本中 + - lua-resty-string v0.10开始AES salt必须是[8个字符](https://github.com/openresty/lua-resty-string/commit/69df3dcc2230364a54761a0d5a65327c6a4e256a) +- 使用内置的session插件时`session_aes_secret`不再是必须配置 + - 若不填则默认为`12345678` + - 若不足8个字符则以`0`补足 + - 若超过8个字符则只使用前8个 + +### v0.3.3 2017.08.05 + +- 使用严格的路由节点id策略,避免潜在冲突 + + +### v0.3.2 2017.06.10 + +- 关于内置session插件的更改 + - 修复session过期时间bug + - 移除lua-resty-session依赖 + - 内置session插件替换为基于cookie的简单实现 + - 接口仍然保持与之前版本兼容 + - 关于session处理,仍然建议根据具体业务需求和安全考量自行实现 +- 支持URI中含有字符'-' + +### v0.3.1 2017.04.16 + +- 支持路由中包含`~`字符(from [@XadillaX](https://github.com/XadillaX)) +- 支持组路由(group router)的多级路由写法 +- 支持组路由下直接挂载中间件(see [issues#40](https://github.com/sumory/lor/issues/40)) + +### v0.3.0 2017.02.11 + +此版本为性能优化和内部实现重构版本,API使用上保持与之前版本兼容,详细描述如下: + +**特性** + +- 中间件(middlewares)重构,支持任意级别、多种方式挂载中间件,这些中间件包括 + - 预处理中间件(use) + - 错误处理中间件(erroruse) + - 业务处理中间件(get/post/put/delete...) +- 提高路由性能 + - 路由匹配次数不再随路由数增多而正比例增长 + - 全面支持正则路由和通配符路由 +- use、get/put/delete/post等API优化,如支持数组参数、支持单独挂载中间件等改进 +- 路由匹配更加灵活: 优先匹配精确路由,其次再匹配正则路由或通配符路由 + +**Break Changes** + +与之前版本相比,break changes主要有以下几点(基本都是一些比较少用到的特性) + +- 路由执行顺序不再与路由定义顺序相关, 如错误路由不用必须定义在最下方 +- 如果一个请求最终匹配不到已定义的任何路由,则不会执行任何中间件代码(之前的版本会执行,这浪费了一些性能) + + +### v0.2.6 2016.11.26 + +- 升级内部集成的session中间件 + - lua-resty-session升级到2.13版本 + - 添加一个session过期参数timeout,默认为3600秒 + - 添加一个refresh_cookie参数,用于控制否在有新请求时刷新session和cookie过期时间,默认“是” +- 更新`lord new`项目模板 + - 缓存`app`对象,提高性能 + - 调整CRUD示例, 详细请参看脚手架代码中的app/routes/user.lua +- 删除默认响应头X-Powered-By + +### v0.2.4 2016.11.16 + +- 支持"application/json"类型请求 + + +### v0.2.2 2016.10.15 + +- 支持opm, 可通过`opm install sumory/lor`安装 + - 注意opm暂不支持命令安装, 所以这种方式无法安装`lord`命令 +- 若仍想使用`lord`命令,建议使用`sh install.sh`方式安装 + +### v0.1.6 2016.10.14 + +- `lord`工具改为使用resty-cli实现,不再依赖luajit + +### v0.1.5 2016.10.01 + +- Path URI支持"." +- 使用xpcall替换pcall以记录更多出错日志 +- 更新了测试用例 + +### v0.1.4 2016.07.30 + +- 删除一些无用代码和引用 +- 升级测试依赖库 +- 修改文档和注释 +- 修改一些小bug + +### v0.1.0 2016.03.15 + +- 增加一配置项,是否启用模板功能:app:conf("view enable", true), 默认为关闭 +- view.lua中ngx.var.template_root存在性判断 +- 增加docker支持 +- 命令`lord --path`变更为`lord path`,用于查看当前lor的安装路径 +- 官网文档更新[http://lor.sumory.com](http://lor.sumory.com) + +### v0.0.9 2016.03.02 + +- 使用install.sh安装lor时如果有指定安装目录,则在指定的目录后面拼上"lor",避免文件误删的问题 +- TODO: debug时列出整个路由表供参考 + +### v0.0.8 2016.02.26 + +- 支持multipart/form文件上传 +- 修复了一个group router被多次app:use时出现404的bug +- 支持Response:json(data, flag)方法传入第二个bool类型参数flag,指明序列化json时默认的空table是否编码为{} + - true 作为{}处理 + - false 作为[]处理 + - 不传入第二个参数则当作[]处理 + + +### v0.0.7 2016.02.02 + +- 统一代码风格 +- 优化部分代码,比如使用ngx.re代替string对应方法、尽量使用local等 +- Break API: req:isFound() -> req:is_found() +- Fix bug: 修复了在lua_code_cache on时的一些404问题 + + +### v0.0.6 2016.01.30 + +- 修改了lor的默认安装路径到/usr/local/lor +- 命令行工具`lord`生成的项目模板更改 + - 加入了nginx.conf配置,方便之后维护自定义的nginx配置 + - 加入start/stop/restart脚本,方便之后项目的灵活部署 +- 改善了路由pattern,支持path variable含有"-"字符 +- 增加了几个测试用例 +- 修复了上一个请求的path variable会污染之后请求的bug +- 完善了res:redirect API +- 修复了请求体为空时解析的bug +- 给lor对象添加了版本号 +- 添加了静态文件支持(通过在nginx.conf里配置) +- 编写了lor框架示例项目[lor-example](https://github.com/lorlabs/lor-example) + + +### v0.0.5 2016.01.28 + +- 完善了Documents和API文档,详见[lor官网](http://lor.sumory.com) +- `lor new`命令生成的项目模板增加了一个middleware目录,用于存放自定义插件 + - 该目录的命名和位置都是非强制的,用户可按需要将自定义的插件放在任何地方 +- 修改了lor new产生的项目模板,增加了几个基本API的使用方式 + + +### v0.0.4 2016.01.25 + +- 以默认插件的形式添加cookie支持(lua-resty-cookie) +- 以默认插件的形式添加session支持(lua-resty-session) + + +### v0.0.3 2016.01.23 + +- 修复上版本路由bug +- 添加模板支持(lua-resty-template) +- 完善了40余个常规测试用例 +- 完善了命令行工具`lord` +- 常规API使用方法添加到默认项目模板 + + +### v0.0.2 2016.01.21 + +- 完全重构v0.0.1路由 +- Sinatra风格路由 +- 主要API设计完成并实现 + + +### v0.0.1 2016.01.15 + +- 原型设计和实验 diff --git a/website/backend/lor/LICENSE b/website/backend/lor/LICENSE new file mode 100755 index 0000000..58c556a --- /dev/null +++ b/website/backend/lor/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 - 2017 sumory.wu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/website/backend/lor/Makefile b/website/backend/lor/Makefile new file mode 100644 index 0000000..068a826 --- /dev/null +++ b/website/backend/lor/Makefile @@ -0,0 +1,49 @@ +TO_INSTALL = lib/* resty spec bin +LOR_HOME ?= /usr/local +LORD_BIN ?= /usr/local/bin + +.PHONY: test install + +test: + busted spec/* + +install_lor: + @mkdir -p ${LOR_HOME}/lor + @mkdir -p ${LOR_HOME} + @rm -rf ${LOR_HOME}/lor/* + + @echo "install lor runtime files to "${LOR_HOME}/lor + + @for item in $(TO_INSTALL) ; do \ + cp -a $$item ${LOR_HOME}/lor/; \ + done; + + @echo "lor runtime files installed." + + +install_lord: install_lor + @mkdir -p ${LORD_BIN} + @echo "install lord cli to "${LORD_BIN}"/" + + @echo "#!/usr/bin/env resty" > tmp_lor_bin + @echo "package.path=\""${LOR_HOME}/lor"/?.lua;;\"" >> tmp_lor_bin + @echo "if arg[1] and arg[1] == \"path\" then" >> tmp_lor_bin + @echo " print(\"${LOR_HOME}/lor\")" >> tmp_lor_bin + @echo " return" >> tmp_lor_bin + @echo "end" >> tmp_lor_bin + @echo "require('bin.lord')(arg)" >> tmp_lor_bin + + @mv tmp_lor_bin ${LORD_BIN}/lord + @chmod +x ${LORD_BIN}/lord + + @echo "lord cli installed." + +install: install_lord + @echo "lor framework installed successfully." + +version: + @lord -v + +help: + @lord -h + diff --git a/website/backend/lor/README.md b/website/backend/lor/README.md new file mode 100755 index 0000000..012f286 --- /dev/null +++ b/website/backend/lor/README.md @@ -0,0 +1,143 @@ +# Lor + +[![https://travis-ci.org/sumory/lor.svg?branch=master](https://travis-ci.org/sumory/lor.svg?branch=master)](https://travis-ci.org/sumory/lor) [![GitHub release](https://img.shields.io/github/release/sumory/lor.svg)](https://github.com/sumory/lor/releases/latest) [![license](https://img.shields.io/github/license/sumory/lor.svg)](https://github.com/sumory/lor/blob/master/LICENSE) + +中文 English + +A fast and minimalist web framework based on [OpenResty](http://openresty.org). + + + +```lua +local lor = require("lor.index") +local app = lor() + +app:get("/", function(req, res, next) + res:send("hello world!") +end) + +app:run() +``` + +## Examples + +- [lor-example](https://github.com/lorlabs/lor-example) +- [openresty-china](https://github.com/sumory/openresty-china) + + +## Installation + +1) shell + +```shell +git clone https://github.com/sumory/lor +cd lor +make install +``` + +`LOR_HOME` and `LORD_BIN` are supported by `Makefile`, so the following command could be used to customize installation: + +``` +make install LOR_HOME=/path/to/lor LORD_BIN=/path/to/lord +``` + +2) opm + +`opm install` is supported from v0.2.2. + +``` +opm install sumory/lor +``` + +`lord` cli is not supported with this installation. + +3) homebrew + +you can use [homebrew-lor](https://github.com/syhily/homebrew-lor) on Mac OSX. + +``` +$ brew tap syhily/lor +$ brew install lor +``` + + +## Features + +- Routing like [Sinatra](http://www.sinatrarb.com/) which is a famous Ruby framework +- Similar API with [Express](http://expressjs.com), good experience for Node.js or Javascript developers +- Middleware support +- Group router support +- Session/Cookie/Views supported and could be redefined with `Middleware` +- Easy to build HTTP APIs, web site, or single page applications + + +## Docs & Community + +- [Website and Documentation](http://lor.sumory.com). +- [Github Organization](https://github.com/lorlabs) for Official Middleware & Modules. + + +## Quick Start + +A quick way to get started with lor is to utilize the executable cli tool `lord` to generate an scaffold application. + +`lord` is installed with `lor` framework. it looks like: + +```bash +$ lord -h +lor ${version}, a Lua web framework based on OpenResty. + +Usage: lord COMMAND [OPTIONS] + +Commands: + new [name] Create a new application + start Starts the server + stop Stops the server + restart Restart the server + version Show version of lor + help Show help tips +``` + +Create app: + +``` +$ lord new lor_demo +``` + +Start server: + +``` +$ cd lor_demo && lord start +``` + +Visit [http://localhost:8888](http://localhost:8888). + + +## Tests + +Install [busted](http://olivinelabs.com/busted/), then run test + +``` +busted spec/* +``` + +## Homebrew + +[https://github.com/syhily/homebrew-lor](https://github.com/syhily/homebrew-lor) maintained by [@syhily](https://github.com/syhily) + +## Contributors + +- [@ms2008](https://github.com/ms2008) +- [@wanghaisheng](https://github.com/wanghaisheng) +- [@lihuibin](https://github.com/lihuibin) +- [@syhily](https://github.com/syhily) +- [@vinsonzou](https://github.com/vinsonzou) +- [@lhmwzy](https://github.com/lhmwzy) +- [@hanxi](https://github.com/hanxi) +- [@诗兄](https://github.com/269724033) +- [@hetz](https://github.com/hetz) +- [@XadillaX](https://github.com/XadillaX) + +## License + +[MIT](./LICENSE) diff --git a/website/backend/lor/README_zh.md b/website/backend/lor/README_zh.md new file mode 100755 index 0000000..6b33e07 --- /dev/null +++ b/website/backend/lor/README_zh.md @@ -0,0 +1,168 @@ +# Lor + +[![https://travis-ci.org/sumory/lor.svg?branch=master](https://travis-ci.org/sumory/lor.svg?branch=master)](https://travis-ci.org/sumory/lor) [![GitHub release](https://img.shields.io/github/release/sumory/lor.svg)](https://github.com/sumory/lor/releases/latest) [![license](https://img.shields.io/github/license/sumory/lor.svg)](https://github.com/sumory/lor/blob/master/LICENSE) + +中文 English + +**Lor**是一个运行在[OpenResty](http://openresty.org)上的基于Lua编写的Web框架. + +- 路由采用[Sinatra](http://www.sinatrarb.com/)风格,结构清晰,易于编码和维护. +- API借鉴了[Express](http://expressjs.com)的思路和设计,Node.js跨界开发者可以很快上手. +- 支持多种路由,路由可分组,路由匹配支持正则模式. +- 支持middleware机制,可在任意路由上挂载中间件. +- 可作为HTTP API Server,也可用于构建传统的Web应用. + + +### 文档 + +[http://lor.sumory.com](http://lor.sumory.com) + +#### 示例项目 + +- 简单示例项目[lor-example](https://github.com/lorlabs/lor-example) +- 全站示例项目[openresty-china](https://github.com/sumory/openresty-china) + + +### 快速开始 + +**特别注意:** 在使用lor之前请首先确保OpenResty已安装,并将`nginx`/`resty`命令配置到环境变量中。即在命令行直接输入`nginx -v`、`resty -v`能正确执行。 + +一个简单示例(更复杂的示例或项目模板请使用`lord`命令生成): + +```lua +local lor = require("lor.index") +local app = lor() + +app:get("/", function(req, res, next) + res:send("hello world!") +end) + +-- 路由示例: 匹配/query/123?foo=bar +app:get("/query/:id", function(req, res, next) + local foo = req.query.foo + local path_id = req.params.id + res:json({ + foo = foo, + id = path_id + }) +end) + +-- 错误处理插件,可根据需要定义多个 +app:erroruse(function(err, req, res, next) + -- err是错误对象 + ngx.log(ngx.ERR, err) + if req:is_found() ~= true then + return res:status(404):send("sorry, not found.") + end + res:status(500):send("server error") +end) + +app:run() +``` + +### 安装 + + +#### 1)使用脚本安装(推荐) + +使用Makefile安装lor框架: + +```shell +git clone https://github.com/sumory/lor +cd lor +make install +``` + +默认`lor`的运行时lua文件会被安装到`/usr/local/lor`下, 命令行工具`lord`被安装在`/usr/local/bin`下。 + +如果希望自定义安装目录, 可参考如下命令自定义路径: + +```shell +make install LOR_HOME=/path/to/lor LORD_BIN=/path/to/lord +``` + +执行**默认安装**后, lor的命令行工具`lord`就被安装在了`/usr/local/bin`下, 通过`which lord`查看: + +``` +$ which lord +/usr/local/bin/lord +``` + +`lor`的运行时包安装在了指定目录下, 可通过`lord path`命令查看。 + + +#### 2)使用opm安装 + +`opm`是OpenResty即将推出的官方包管理器,从v0.2.2开始lor支持通过opm安装: + +``` +opm install sumory/lor +``` + +注意: 目前opm不支持安装命令行工具,所以此种方式安装后不能使用`lord`命令。 + + +#### 3)使用homebrew安装 + +除使用以上方式安装外, Mac用户还可使用homebrew来安装lor, 该方式由[@syhily](https://github.com/syhily)提供, 更详尽的使用方法请参见[这里](https://github.com/syhily/homebrew-lor)。 + +``` +$ brew tap syhily/lor +$ brew install lor +``` + +至此, `lor`框架已经安装完毕,接下来使用`lord`命令行工具快速开始一个项目骨架. + + +### 使用 + +``` +$ lord -h +lor ${version}, a Lua web framework based on OpenResty. + +Usage: lord COMMAND [OPTIONS] + +Commands: + new [name] Create a new application + start Starts the server + stop Stops the server + restart Restart the server + version Show version of lor + help Show help tips +``` + +执行`lord new lor_demo`,则会生成一个名为lor_demo的示例项目,然后执行: + +``` +cd lor_demo +lord start +``` + +之后访问[http://localhost:8888/](http://localhost:8888/), 即可。 + +更多使用方法,请参考[use cases](./spec/cases)测试用例。 + +### Homebrew + +[https://github.com/syhily/homebrew-lor](https://github.com/syhily/homebrew-lor)由[@syhily](https://github.com/syhily)维护。 + +### 贡献者 + +- [@ms2008](https://github.com/ms2008) +- [@wanghaisheng](https://github.com/wanghaisheng) +- [@lihuibin](https://github.com/lihuibin) +- [@syhily](https://github.com/syhily) +- [@vinsonzou](https://github.com/vinsonzou) +- [@lhmwzy](https://github.com/lhmwzy) +- [@hanxi](https://github.com/hanxi) +- [@诗兄](https://github.com/269724033) +- [@hetz](https://github.com/hetz) +- [@XadillaX](https://github.com/XadillaX) + +### 讨论交流 + +有一个QQ群用于在线讨论: 522410959 + +### License + +[MIT](./LICENSE) diff --git a/website/backend/lor/bin/lord.lua b/website/backend/lor/bin/lord.lua new file mode 100755 index 0000000..7c7f2e4 --- /dev/null +++ b/website/backend/lor/bin/lord.lua @@ -0,0 +1,47 @@ +package.path = './?.lua;' .. package.path + +local generator = require("bin.scaffold.generator") +local lor = require("bin.scaffold.launcher") +local version = require("lor.version") + +local usages = [[lor v]] .. version .. [[, a Lua web framework based on OpenResty. + +Usage: lord COMMAND [OPTIONS] + +Commands: + new [name] Create a new application + start Start running app server + stop Stop the server + restart Restart the server + version Show version of lor + help Show help tips + path Show install path +]] + +local function exec(args) + local arg = table.remove(args, 1) + + -- parse commands and options + if arg == 'new' and args[1] then + generator.new(args[1]) -- generate example code + elseif arg == 'start' then + lor.start() -- start application + elseif arg == 'stop' then + lor.stop() -- stop application + elseif arg == 'restart' then + lor.stop() + lor.start() + elseif arg == 'reload' then + lor.reload() + elseif arg == 'help' or arg == '-h' then + print(usages) + elseif arg == 'version' or arg == '-v' then + print(version) -- show lor framework version + elseif arg == nil then + print(usages) + else + print("[lord] unsupported commands or options, `lord -h` to check usages.") + end +end + +return exec diff --git a/website/backend/lor/bin/scaffold/generator.lua b/website/backend/lor/bin/scaffold/generator.lua new file mode 100755 index 0000000..4e9fe87 --- /dev/null +++ b/website/backend/lor/bin/scaffold/generator.lua @@ -0,0 +1,628 @@ +local sgmatch = string.gmatch +local utils = require 'bin.scaffold.utils' + +local gitignore = [[ +# lor +client_body_temp +fastcgi_temp +logs +proxy_temp +tmp +uwsgi_temp + +# Compiled Lua sources +luac.out + +# luarocks build files +*.src.rock +*.zip +*.tar.gz + +# Object files +*.o +*.os +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo +*.def +*.exp + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex +]] + +local mime_types = [[ +types { + text/html html htm shtml; + text/css css; + text/xml xml; + image/gif gif; + image/jpeg jpeg jpg; + application/javascript js; + application/atom+xml atom; + application/rss+xml rss; + + text/mathml mml; + text/plain txt; + text/vnd.sun.j2me.app-descriptor jad; + text/vnd.wap.wml wml; + text/x-component htc; + + image/png png; + image/tiff tif tiff; + image/vnd.wap.wbmp wbmp; + image/x-icon ico; + image/x-jng jng; + image/x-ms-bmp bmp; + image/svg+xml svg svgz; + image/webp webp; + + application/font-woff woff; + application/java-archive jar war ear; + application/json json; + application/mac-binhex40 hqx; + application/msword doc; + application/pdf pdf; + application/postscript ps eps ai; + application/rtf rtf; + application/vnd.apple.mpegurl m3u8; + application/vnd.ms-excel xls; + application/vnd.ms-fontobject eot; + application/vnd.ms-powerpoint ppt; + application/vnd.wap.wmlc wmlc; + application/vnd.google-earth.kml+xml kml; + application/vnd.google-earth.kmz kmz; + application/x-7z-compressed 7z; + application/x-cocoa cco; + application/x-java-archive-diff jardiff; + application/x-java-jnlp-file jnlp; + application/x-makeself run; + application/x-perl pl pm; + application/x-pilot prc pdb; + application/x-rar-compressed rar; + application/x-redhat-package-manager rpm; + application/x-sea sea; + application/x-shockwave-flash swf; + application/x-stuffit sit; + application/x-tcl tcl tk; + application/x-x509-ca-cert der pem crt; + application/x-xpinstall xpi; + application/xhtml+xml xhtml; + application/xspf+xml xspf; + application/zip zip; + + application/octet-stream bin exe dll; + application/octet-stream deb; + application/octet-stream dmg; + application/octet-stream iso img; + application/octet-stream msi msp msm; + + application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; + application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; + + audio/midi mid midi kar; + audio/mpeg mp3; + audio/ogg ogg; + audio/x-m4a m4a; + audio/x-realaudio ra; + + video/3gpp 3gpp 3gp; + video/mp2t ts; + video/mp4 mp4; + video/mpeg mpeg mpg; + video/quicktime mov; + video/webm webm; + video/x-flv flv; + video/x-m4v m4v; + video/x-mng mng; + video/x-ms-asf asx asf; + video/x-ms-wmv wmv; + video/x-msvideo avi; +} +]] + + +local index_view_tpl = [[ + + + + + + + + +]] + +local user_info_view_tpl = [[ + + + + + +
+ {{desc}}
+ {{id}} + {{name}} +
+ + +]] + +local main_tpl = [[ +local app = require("app.server") +app:run() +]] + +local server_tpl = [[ +local string_find = string.find +local lor = require("lor.index") +local router = require("app.router") +local app = lor() + +-- 模板配置 +app:conf("view enable", true) +app:conf("view engine", "tmpl") +app:conf("view ext", "html") +app:conf("view layout", "") +app:conf("views", "./app/views") + +-- session和cookie支持,如果不需要可注释以下配置 +local mw_cookie = require("lor.lib.middleware.cookie") +local mw_session = require("lor.lib.middleware.session") +app:use(mw_cookie()) +app:use(mw_session({ + session_key = "__app__", -- the key injected in cookie + session_aes_key = "aes_key_for_session", -- should set by yourself + timeout = 3600 -- default session timeout is 3600 seconds +})) + +-- 自定义中间件1: 注入一些全局变量供模板渲染使用 +local mw_inject_version = require("app.middleware.inject_app_info") +app:use(mw_inject_version()) + +-- 自定义中间件2: 设置响应头 +app:use(function(req, res, next) + res:set_header("X-Powered-By", "Lor framework") + next() +end) + +router(app) -- 业务路由处理 + +-- 错误处理插件,可根据需要定义多个 +app:erroruse(function(err, req, res, next) + ngx.log(ngx.ERR, err) + + if req:is_found() ~= true then + if string_find(req.headers["Accept"], "application/json") then + res:status(404):json({ + success = false, + msg = "404! sorry, not found." + }) + else + res:status(404):send("404! sorry, not found. " .. (req.path or "")) + end + else + if string_find(req.headers["Accept"], "application/json") then + res:status(500):json({ + success = false, + msg = "500! internal error, please check the log." + }) + else + res:status(500):send("internal error, please check the log.") + end + end +end) + +return app +]] + + +local router_tpl = [[ +-- 业务路由管理 +local userRouter = require("app.routes.user") + +return function(app) + + -- simple router: hello world! + app:get("/hello", function(req, res, next) + res:send("hi! welcome to lor framework.") + end) + + -- simple router: render html, visit "/" or "/?name=foo&desc=bar + app:get("/", function(req, res, next) + local data = { + name = req.query.name or "lor", + desc = req.query.desc or 'a framework of lua based on OpenResty' + } + res:render("index", data) + end) + + -- group router: 对以`/user`开始的请求做过滤处理 + app:use("/user", userRouter()) +end + +]] + + +local user_router_tpl = [[ +local lor = require("lor.index") +local userRouter = lor:Router() -- 生成一个group router对象 + +-- 按id查找用户 +-- e.g. /query/123 +userRouter:get("/query/:id", function(req, res, next) + local query_id = tonumber(req.params.id) -- 从req.params取参数 + + if not query_id then + return res:render("user/info", { + desc = "Error to find user, path variable `id` should be a number. e.g. /user/query/123" + }) + end + + -- 渲染页面 + res:render("user/info", { + id = query_id, + name = "user" .. query_id, + desc = "User Information" + }) +end) + +-- 删除用户 +-- e.g. /delete?id=123 +userRouter:delete("/delete", function(req, res, next) + local id = req.query.id -- 从req.query取参数 + if not id then + return res:html("

Error: query param id is required.

") + end + + -- 返回html + res:html("succeed to delete user
user id is:" .. id .. "") +end) + +-- 修改用户 +-- e.g. /put/123?name=sumory +userRouter:put("/put/:id", function(req, res, next) + local id = req.params.id -- 从req.params取参数 + local name = req.query.name -- 从req.query取参数 + + if not id or not name then + return res:send("error params: id and name are required.") + end + + -- 返回文本格式的响应结果 + res:send("succeed to modify user[" .. id .. "] with new name:" .. name) +end) + +-- 创建用户 +userRouter:post("/post", function(req, res, next) + local content_type = req.headers['Content-Type'] + + -- 如果请求类型为form表单或json请求体 + if string.find(content_type, "application/x-www-form-urlencoded",1, true) or + string.find(content_type, "application/json",1, true) then + local id = req.body.id -- 从请求体取参数 + local name = req.body.name -- 从请求体取参数 + + if not id or not name then + return res:json({ + success = false, + msg = "error params: id and name are required." + }) + end + + res:json({-- 返回json格式的响应体 + success = true, + data = { + id = id, + name = name, + desc = "succeed to create new user" .. id + } + }) + else -- 不支持其他请求体 + res:status(500):send("not supported request Content-Type[" .. content_type .. "]") + end +end) + +return userRouter +]] + + + +local middleware_tpl = [[ + +### 自定义插件目录(define your own middleware) + + +You are recommended to define your own middlewares and keep them in one place to manage. + +建议用户将自定义插件存放在此目录下统一管理,然后在其他地方引用,插件的格式如下: + +``` +local middleware = function(params) + return function(req, res, next) + -- do something with req/res + next() + end +end + +return middleware +``` + +]] + + +local middleware_example_tpl = [[ +--- 中间件示例: 为每个请求注入一些通用的变量 +local lor = require("lor.index") +return function() + return function(req, res, next) + -- res.locals是一个table, 可以在这里注入一些“全局”变量 + -- 这个示例里注入app的名称和版本号, 在渲染页面时即可使用 + res.locals.app_name = "lor application" + res.locals.app_version = lor.version or "" + next() + end +end +]] + + +local static_tpl = [[ + +### 静态文件目录(static files directory) + +nginx对应配置为 + +``` +location /static { + alias app/static; +} +``` + +]] + +local ngx_conf_directory = [[ + +### nginx configuration directory + +]] + + +local ngx_config = require 'bin.scaffold.nginx.config' +local ngx_conf_template = require 'bin.scaffold.nginx.conf_template' +local function nginx_conf_content() + -- read nginx.conf file + local nginx_conf_template = ngx_conf_template.get_ngx_conf_template() + + -- append notice + nginx_conf_template = [[ +#generated by `lor framework` + ]] .. nginx_conf_template + + local match = {} + local tmp = 1 + for v in sgmatch(nginx_conf_template , '{{(.-)}}') do + match[tmp] = v + tmp = tmp + 1 + end + + for _, directive in ipairs(match) do + if ngx_config[directive] ~= nil then + nginx_conf_template = string.gsub(nginx_conf_template, '{{' .. directive .. '}}', ngx_config[directive]) + else + nginx_conf_template = string.gsub(nginx_conf_template, '{{' .. directive .. '}}', '#' .. directive) + end + end + + return nginx_conf_template +end +local ngx_conf_tpl = nginx_conf_content() + + +local start_sh = [[ +#!/bin/sh + +##################################################################### +# usage: +# sh start.sh -- start application @dev +# sh start.sh ${env} -- start application @${env} + +# examples: +# sh start.sh prod -- use conf/nginx-prod.conf to start OpenResty +# sh start.sh -- use conf/nginx-dev.conf to start OpenResty +##################################################################### + +if [ -n "$1" ];then + PROFILE="$1" +else + PROFILE=dev +fi + +mkdir -p logs & mkdir -p tmp +echo "start lor application with profile: "${PROFILE} +nginx -p `pwd`/ -c conf/nginx-${PROFILE}.conf +]] + + +local stop_sh = [[ +#!/bin/sh + +##################################################################### +# usage: +# sh stop.sh -- stop application @dev +# sh stop.sh ${env} -- stop application @${env} + +# examples: +# sh stop.sh prod -- use conf/nginx-prod.conf to stop OpenResty +# sh stop.sh -- use conf/nginx-dev.conf to stop OpenResty +##################################################################### + +if [ -n "$1" ];then + PROFILE="$1" +else + PROFILE=dev +fi + +mkdir -p logs & mkdir -p tmp +echo "stop lor application with profile: "${PROFILE} +nginx -s stop -p `pwd`/ -c conf/nginx-${PROFILE}.conf +]] + +local reload_sh = [[ +#!/bin/sh + +##################################################################### +# usage: +# sh reload.sh -- reload application @dev +# sh reload.sh ${env} -- reload application @${env} + +# examples: +# sh reload.sh prod -- use conf/nginx-prod.conf to reload OpenResty +# sh reload.sh -- use conf/nginx-dev.conf to reload OpenResty +##################################################################### + +if [ -n "$1" ];then + PROFILE="$1" +else + PROFILE=dev +fi + +mkdir -p logs & mkdir -p tmp +echo "reload lor application with profile: "${PROFILE} +nginx -s reload -p `pwd`/ -c conf/nginx-${PROFILE}.conf +]] + + + +local Generator = {} + +Generator.files = { + ['.gitignore'] = gitignore, + ['app/main.lua'] = main_tpl, + ['app/server.lua'] = server_tpl, + ['app/router.lua'] = router_tpl, + ['app/routes/user.lua'] = user_router_tpl, + ['app/views/index.html'] = index_view_tpl, + ['app/views/user/info.html'] = user_info_view_tpl, + ['app/middleware/README.md'] = middleware_tpl, + ['app/middleware/inject_app_info.lua'] = middleware_example_tpl, + ['app/static/README.md'] = static_tpl, -- static files directory,e.g. js/css/img + ['conf/README.md'] = ngx_conf_directory, -- nginx config directory + ['conf/nginx-dev.conf'] = ngx_conf_tpl, -- nginx config file + ['conf/mime.types'] = mime_types, -- nginx mime + ['start.sh'] = start_sh, + ['stop.sh'] = stop_sh, + ['reload.sh'] = reload_sh +} + +function Generator.new(name) + print('Creating app: ' .. name .. '...') + Generator.create_files(name) +end + +function Generator.create_files(parent) + for file_path, file_content in pairs(Generator.files) do + + local full_file_path = parent .. '/' .. file_path + local full_file_dirname = utils.dirname(full_file_path) + os.execute('mkdir -p ' .. full_file_dirname .. ' > /dev/null') + + local fw = io.open(full_file_path, 'w') + fw:write(file_content) + fw:close() + print(' created file ' .. full_file_path) + end +end + +return Generator + + + + diff --git a/website/backend/lor/bin/scaffold/launcher.lua b/website/backend/lor/bin/scaffold/launcher.lua new file mode 100755 index 0000000..da227c7 --- /dev/null +++ b/website/backend/lor/bin/scaffold/launcher.lua @@ -0,0 +1,101 @@ +local sgmatch = string.gmatch +local ogetenv = os.getenv + +local ngx_handle = require 'bin.scaffold.nginx.handle' +local ngx_config = require 'bin.scaffold.nginx.config' +local ngx_conf_template = require 'bin.scaffold.nginx.conf_template' + + +local Lor = {} + +function Lor.nginx_conf_content() + -- read nginx.conf file + local nginx_conf_template = ngx_conf_template.get_ngx_conf_template() + + -- append notice + nginx_conf_template = [[ +#generated by `lor framework` + ]] .. nginx_conf_template + + local match = {} + local tmp = 1 + for v in sgmatch(nginx_conf_template , '{{(.-)}}') do + match[tmp] = v + tmp = tmp + 1 + end + + for _, directive in ipairs(match) do + if ngx_config[directive] ~= nil then + nginx_conf_template = string.gsub(nginx_conf_template, '{{' .. directive .. '}}', ngx_config[directive]) + else + nginx_conf_template = string.gsub(nginx_conf_template, '{{' .. directive .. '}}', '#' .. directive) + end + end + + return nginx_conf_template +end + +function new_handler() + local necessary_dirs ={ -- runtime nginx conf/pid/logs dir + tmp = 'tmp', + logs = 'logs' + } + local env = ogetenv("LOR_ENV") or 'dev' + return ngx_handle.new( + necessary_dirs, + Lor.nginx_conf_content(), + "conf/nginx-" .. env .. ".conf" + ) +end + +function Lor.start() + local env = ogetenv("LOR_ENV") or 'dev' + + local ok, handler = pcall(function() return new_handler() end) + if ok == false then + print("ERROR:Cannot initialize handler: " .. handler) + return + end + + local result = handler:start(env) + if result == 0 or result == true or result == "true" then + if env ~= 'test' then + print("app in " .. env .. " was succesfully started on port " .. ngx_config.PORT) + end + else + print("ERROR: Could not start app on port " .. ngx_config.PORT) + end +end + + +function Lor.stop() + local env = ogetenv("LOR_ENV") or 'dev' + + local handler = new_handler() + local result = handler:stop(env) + + if env ~= 'test' then + if result == 0 or result == true or result == "true" then + print("app in " .. env .. " was succesfully stopped.") + else + print("ERROR: Could not stop app (are you sure it is running?).") + end + end +end + +function Lor.reload() + local env = ogetenv("LOR_ENV") or 'dev' + + local handler = new_handler() + local result = handler:reload(env) + + if env ~= 'test' then + if result == 0 or result == true or result == "true" then + print("app in " .. env .. " was succesfully reloaded.") + else + print("ERROR: Could not reloaded app.") + end + end +end + +return Lor diff --git a/website/backend/lor/bin/scaffold/nginx/conf_template.lua b/website/backend/lor/bin/scaffold/nginx/conf_template.lua new file mode 100755 index 0000000..eb27ce6 --- /dev/null +++ b/website/backend/lor/bin/scaffold/nginx/conf_template.lua @@ -0,0 +1,47 @@ +local _M = {} + +function _M:get_ngx_conf_template() + return [[ +# user www www; +pid tmp/{{LOR_ENV}}-nginx.pid; + +# This number should be at maxium the number of CPU on the server +worker_processes 4; + +events { + # Number of connections per worker + worker_connections 4096; +} + +http { + sendfile on; + include ./mime.types; + + {{LUA_PACKAGE_PATH}} + lua_code_cache on; + + server { + # List port + listen {{PORT}}; + + # Access log + access_log logs/{{LOR_ENV}}-access.log; + + # Error log + error_log logs/{{LOR_ENV}}-error.log; + + # this variable is for view render(lua-resty-template) + set $template_root ''; + + location /static { + alias {{STATIC_FILE_DIRECTORY}}; #app/static; + } + + # lor runtime + {{CONTENT_BY_LUA_FILE}} + } +} + ]] +end + +return _M diff --git a/website/backend/lor/bin/scaffold/nginx/config.lua b/website/backend/lor/bin/scaffold/nginx/config.lua new file mode 100755 index 0000000..187669c --- /dev/null +++ b/website/backend/lor/bin/scaffold/nginx/config.lua @@ -0,0 +1,69 @@ +local pairs = pairs +local ogetenv = os.getenv +local utils = require 'bin.scaffold.utils' +local app_run_env = ogetenv("LOR_ENV") or 'dev' + +local lor_ngx_conf = {} +lor_ngx_conf.common = { -- directives + LOR_ENV = app_run_env, + -- INIT_BY_LUA_FILE = './app/nginx/init.lua', + -- LUA_PACKAGE_PATH = '', + -- LUA_PACKAGE_CPATH = '', + CONTENT_BY_LUA_FILE = './app/main.lua', + STATIC_FILE_DIRECTORY = './app/static' +} + +lor_ngx_conf.env = {} +lor_ngx_conf.env.dev = { + LUA_CODE_CACHE = false, + PORT = 8888 +} + +lor_ngx_conf.env.test = { + LUA_CODE_CACHE = true, + PORT = 9999 +} + +lor_ngx_conf.env.prod = { + LUA_CODE_CACHE = true, + PORT = 80 +} + +local function getNgxConf(conf_arr) + if conf_arr['common'] ~= nil then + local common_conf = conf_arr['common'] + local env_conf = conf_arr['env'][app_run_env] + for directive, info in pairs(common_conf) do + env_conf[directive] = info + end + return env_conf + elseif conf_arr['env'] ~= nil then + return conf_arr['env'][app_run_env] + end + return {} +end + +local function buildConf() + local sys_ngx_conf = getNgxConf(lor_ngx_conf) + return sys_ngx_conf +end + + +local ngx_directive_handle = require('bin.scaffold.nginx.directive'):new(app_run_env) +local ngx_directives = ngx_directive_handle:directiveSets() +local ngx_run_conf = buildConf() + +local LorNgxConf = {} +for directive, func in pairs(ngx_directives) do + if type(func) == 'function' then + local func_rs = func(ngx_directive_handle, ngx_run_conf[directive]) + if func_rs ~= false then + LorNgxConf[directive] = func_rs + end + else + LorNgxConf[directive] = ngx_run_conf[directive] + end +end + +return LorNgxConf + diff --git a/website/backend/lor/bin/scaffold/nginx/directive.lua b/website/backend/lor/bin/scaffold/nginx/directive.lua new file mode 100755 index 0000000..919b01e --- /dev/null +++ b/website/backend/lor/bin/scaffold/nginx/directive.lua @@ -0,0 +1,205 @@ +-- most code is from https://github.com/idevz/vanilla/blob/master/vanilla/sys/nginx/directive.lua + +package.path = './app/?.lua;' .. package.path +package.cpath = './app/library/?.so;' .. package.cpath + + +local Directive = {} + +function Directive:new(env) + local run_env = 'prod' + if env ~= nil then run_env = env end + local instance = { + run_env = run_env, + directiveSets = self.directiveSets + } + setmetatable(instance, Directive) + return instance +end + +function Directive:luaPackagePath(lua_path) + local path = package.path + if lua_path ~= nil then path = lua_path .. path end + local res = [[lua_package_path "]] .. path .. [[;;";]] + return res +end + +function Directive:luaPackageCpath(lua_cpath) + local path = package.cpath + if lua_cpath ~= nil then path = lua_cpath .. path end + local res = [[lua_package_cpath "]] .. path .. [[;;";]] + return res +end + +function Directive:codeCache(bool_var) + if bool_var == true then bool_var = 'on' else bool_var = 'off' end + local res = [[lua_code_cache ]] .. bool_var.. [[;]] + return res +end + +function Directive:luaSharedDict( lua_lib ) + local ok, sh_dict_conf_or_error = pcall(function() return require(lua_lib) end) + if ok == false then + return false + end + local res = '' + if sh_dict_conf_or_error ~= nil then + for name,size in pairs(sh_dict_conf_or_error) do + res = res .. [[lua_shared_dict ]] .. name .. ' ' .. size .. ';' + end + end + return res +end + +function Directive:initByLua(lua_lib) + if lua_lib == nil then return '' end + local res = [[init_by_lua require(']] .. lua_lib .. [['):run();]] + return res +end + +function Directive:initByLuaFile(lua_file) + if lua_file == nil then return '' end + local res = [[init_by_lua_file ]] .. lua_file .. [[;]] + return res +end + +function Directive:initWorkerByLua(lua_lib) + if lua_lib == nil then return '' end + local res = [[init_worker_by_lua require(']] .. lua_lib .. [['):run();]] + return res +end + +function Directive:initWorkerByLuaFile(lua_file) + if lua_file == nil then return '' end + local res = [[init_worker_by_lua_file ]] .. lua_file .. [[;]] + return res +end + +function Directive:setByLua(lua_lib) + if lua_lib == nil then return '' end + local res = [[set_by_lua require(']] .. lua_lib .. [[');]] + return res +end + +function Directive:setByLuaFile(lua_file) + if lua_file == nil then return '' end + local res = [[set_by_lua_file ]] .. lua_file .. [[;]] + return res +end + +function Directive:rewriteByLua(lua_lib) + if lua_lib == nil then return '' end + local res = [[rewrite_by_lua require(']] .. lua_lib .. [['):run();]] + return res +end + +function Directive:rewriteByLuaFile(lua_file) + if lua_file == nil then return '' end + local res = [[rewrite_by_lua_file ]] .. lua_file .. [[;]] + return res +end + +function Directive:accessByLua(lua_lib) + if lua_lib == nil then return '' end + local res = [[access_by_lua require(']] .. lua_lib .. [['):run();]] + return res +end + +function Directive:accessByLuaFile(lua_file) + if lua_file == nil then return '' end + local res = [[access_by_lua_file ]] .. lua_file .. [[;]] + return res +end + +function Directive:contentByLua(lua_lib) + if lua_lib == nil then return '' end + -- local res = [[content_by_lua require(']] .. lua_lib .. [['):run();]] + local res = [[location / { + content_by_lua require(']] .. lua_lib .. [['):run(); + }]] + return res +end + +function Directive:contentByLuaFile(lua_file) + if lua_file == nil then return '' end + local res = [[location / { + content_by_lua_file ]] .. lua_file .. [[; + }]] + return res +end + +function Directive:headerFilterByLua(lua_lib) + if lua_lib == nil then return '' end + local res = [[header_filter_by_lua require(']] .. lua_lib .. [['):run();]] + return res +end + +function Directive:headerFilterByLuaFile(lua_file) + if lua_file == nil then return '' end + local res = [[header_filter_by_lua_file ]] .. lua_file .. [[;]] + return res +end + +function Directive:bodyFilterByLua(lua_lib) + if lua_lib == nil then return '' end + local res = [[body_filter_by_lua require(']] .. lua_lib .. [['):run();]] + return res +end + +function Directive:bodyFilterByLuaFile(lua_file) + if lua_file == nil then return '' end + local res = [[body_filter_by_lua_file ]] .. lua_file .. [[;]] + return res +end + +function Directive:logByLua(lua_lib) + if lua_lib == nil then return '' end + local res = [[log_by_lua require(']] .. lua_lib .. [['):run();]] + return res +end + +function Directive:logByLuaFile(lua_file) + if lua_file == nil then return '' end + local res = [[log_by_lua_file ]] .. lua_file .. [[;]] + return res +end + + +function Directive:staticFileDirectory(static_file_directory) + if static_file_directory == nil then return '' end + return static_file_directory +end + + +function Directive:directiveSets() + return { + ['LOR_ENV'] = self.run_env, + ['PORT'] = 80, + ['NGX_PATH'] = '', + ['LUA_PACKAGE_PATH'] = Directive.luaPackagePath, + ['LUA_PACKAGE_CPATH'] = Directive.luaPackageCpath, + ['LUA_CODE_CACHE'] = Directive.codeCache, + ['LUA_SHARED_DICT'] = Directive.luaSharedDict, + ['INIT_BY_LUA'] = Directive.initByLua, + ['INIT_BY_LUA_FILE'] = Directive.initByLuaFile, + ['INIT_WORKER_BY_LUA'] = Directive.initWorkerByLua, + ['INIT_WORKER_BY_LUA_FILE'] = Directive.initWorkerByLuaFile, + ['SET_BY_LUA'] = Directive.setByLua, + ['SET_BY_LUA_FILE'] = Directive.setByLuaFile, + ['REWRITE_BY_LUA'] = Directive.rewriteByLua, + ['REWRITE_BY_LUA_FILE'] = Directive.rewriteByLuaFile, + ['ACCESS_BY_LUA'] = Directive.accessByLua, + ['ACCESS_BY_LUA_FILE'] = Directive.accessByLuaFile, + ['CONTENT_BY_LUA'] = Directive.contentByLua, + ['CONTENT_BY_LUA_FILE'] = Directive.contentByLuaFile, + ['HEADER_FILTER_BY_LUA'] = Directive.headerFilterByLua, + ['HEADER_FILTER_BY_LUA_FILE'] = Directive.headerFilterByLuaFile, + ['BODY_FILTER_BY_LUA'] = Directive.bodyFilterByLua, + ['BODY_FILTER_BY_LUA_FILE'] = Directive.bodyFilterByLuaFile, + ['LOG_BY_LUA'] = Directive.logByLua, + ['LOG_BY_LUA_FILE'] = Directive.logByLuaFile, + ['STATIC_FILE_DIRECTORY'] = Directive.staticFileDirectory + } +end + +return Directive \ No newline at end of file diff --git a/website/backend/lor/bin/scaffold/nginx/handle.lua b/website/backend/lor/bin/scaffold/nginx/handle.lua new file mode 100755 index 0000000..861f7fa --- /dev/null +++ b/website/backend/lor/bin/scaffold/nginx/handle.lua @@ -0,0 +1,76 @@ +-- most code is from https://github.com/ostinelli/gin/blob/master/gin/cli/base_launcher.lua +local function create_dirs(necessary_dirs) + for _, dir in pairs(necessary_dirs) do + os.execute("mkdir -p " .. dir .. " > /dev/null") + end +end + +local function create_nginx_conf(nginx_conf_file_path, nginx_conf_content) + local fw = io.open(nginx_conf_file_path, "w") + fw:write(nginx_conf_content) + fw:close() +end + +local function remove_nginx_conf(nginx_conf_file_path) + os.remove(nginx_conf_file_path) +end + +local function nginx_command(env, nginx_conf_file_path, nginx_signal) + local env_cmd = "" + + if env ~= nil then env_cmd = "-g \"env LOR_ENV=" .. env .. ";\"" end + local cmd = "nginx " .. nginx_signal .. " " .. env_cmd .. " -p `pwd`/ -c " .. nginx_conf_file_path + print("execute: " .. cmd) + return os.execute(cmd) +end + +local function start_nginx(env, nginx_conf_file_path) + return nginx_command(env, nginx_conf_file_path, '') +end + +local function stop_nginx(env, nginx_conf_file_path) + return nginx_command(env, nginx_conf_file_path, '-s stop') +end + +local function reload_nginx(env, nginx_conf_file_path) + return nginx_command(env, nginx_conf_file_path, '-s reload') +end + + +local NginxHandle = {} +NginxHandle.__index = NginxHandle + +function NginxHandle.new(necessary_dirs, nginx_conf_content, nginx_conf_file_path) + local instance = { + nginx_conf_content = nginx_conf_content, + nginx_conf_file_path = nginx_conf_file_path, + necessary_dirs = necessary_dirs + } + setmetatable(instance, NginxHandle) + return instance +end + +function NginxHandle:start(env) + create_dirs(self.necessary_dirs) + -- create_nginx_conf(self.nginx_conf_file_path, self.nginx_conf_content) + + return start_nginx(env, self.nginx_conf_file_path) +end + +function NginxHandle:stop(env) + local result = stop_nginx(env, self.nginx_conf_file_path) + -- remove_nginx_conf(self.nginx_conf_file_path) + + return result +end + +function NginxHandle:reload(env) + -- remove_nginx_conf(self.nginx_conf_file_path) + create_dirs(self.necessary_dirs) + -- create_nginx_conf(self.nginx_conf_file_path, self.nginx_conf_content) + + return reload_nginx(env, self.nginx_conf_file_path) +end + + +return NginxHandle diff --git a/website/backend/lor/bin/scaffold/utils.lua b/website/backend/lor/bin/scaffold/utils.lua new file mode 100755 index 0000000..3752a2d --- /dev/null +++ b/website/backend/lor/bin/scaffold/utils.lua @@ -0,0 +1,45 @@ +local pcall = pcall +local require = require +local iopen = io.open +local smatch = string.match + +local Utils = {} + +-- read file +function Utils.read_file(file_path) + local f = iopen(file_path, "rb") + local content = f:read("*a") + f:close() + return content +end + + +local function require_module(module_name) + return require(module_name) +end + + +-- try to require +function Utils.try_require(module_name, default) + local ok, module_or_err = pcall(require_module, module_name) + + if ok == true then return module_or_err end + + if ok == false and smatch(module_or_err, "'" .. module_name .. "' not found") then + return default + else + error(module_or_err) + end +end + + +function Utils.dirname(str) + if str:match(".-/.-") then + local name = string.gsub(str, "(.*/)(.*)", "%1") + return name + else + return '' + end +end + +return Utils \ No newline at end of file diff --git a/website/backend/lor/dist.ini b/website/backend/lor/dist.ini new file mode 100755 index 0000000..394003e --- /dev/null +++ b/website/backend/lor/dist.ini @@ -0,0 +1,10 @@ +name = lor +abstract = A fast and minimalist web framework based on OpenResty. +version = 0.3.3 +author = Sumory Wu (@sumory) +is_original = yes +license = mit +repo_link = https://github.com/sumory/lor +main_module = lib/lor/index.lua +exclude_files = .travis, docker, docs, .travis.yml +requires = bungle/lua-resty-template >= 1.9, p0pr0ck5/lua-resty-cookie >= 0.01 diff --git a/website/backend/lor/lib/lor/index.lua b/website/backend/lor/lib/lor/index.lua new file mode 100755 index 0000000..2f13e50 --- /dev/null +++ b/website/backend/lor/lib/lor/index.lua @@ -0,0 +1,27 @@ +local type = type + +local version = require("lor.version") +local Group = require("lor.lib.router.group") +local Router = require("lor.lib.router.router") +local Request = require("lor.lib.request") +local Response = require("lor.lib.response") +local Application = require("lor.lib.application") +local Wrap = require("lor.lib.wrap") + +LOR_FRAMEWORK_DEBUG = false + +local createApplication = function(options) + if options and options.debug and type(options.debug) == 'boolean' then + LOR_FRAMEWORK_DEBUG = options.debug + end + + local app = Application:new() + app:init(options) + + return app +end + +local lor = Wrap:new(createApplication, Router, Group, Request, Response) +lor.version = version + +return lor diff --git a/website/backend/lor/lib/lor/lib/application.lua b/website/backend/lor/lib/lor/lib/application.lua new file mode 100755 index 0000000..8611153 --- /dev/null +++ b/website/backend/lor/lib/lor/lib/application.lua @@ -0,0 +1,181 @@ +local pairs = pairs +local type = type +local xpcall = xpcall +local setmetatable = setmetatable + +local Router = require("lor.lib.router.router") +local Request = require("lor.lib.request") +local Response = require("lor.lib.response") +local View = require("lor.lib.view") +local supported_http_methods = require("lor.lib.methods") + +local router_conf = { + strict_route = true, + ignore_case = true, + max_uri_segments = true, + max_fallback_depth = true +} + +local App = {} + +function App:new() + local instance = {} + instance.cache = {} + instance.settings = {} + instance.router = Router:new() + + setmetatable(instance, { + __index = self, + __call = self.handle + }) + + instance:init_method() + return instance +end + +function App:run(final_handler) + local request = Request:new() + local response = Response:new() + + local enable_view = self:getconf("view enable") + if enable_view then + local view_config = { + view_enable = enable_view, + view_engine = self:getconf("view engine"), -- view engine: resty-template or others... + view_ext = self:getconf("view ext"), -- defautl is "html" + view_layout = self:getconf("view layout"), -- defautl is "" + views = self:getconf("views") -- template files directory + } + + local view = View:new(view_config) + response.view = view + end + + self:handle(request, response, final_handler) +end + +function App:init(options) + self:default_configuration(options) +end + +function App:default_configuration(options) + options = options or {} + + -- view and template configuration + if options["view enable"] ~= nil and options["view enable"] == true then + self:conf("view enable", true) + else + self:conf("view enable", false) + end + self:conf("view engine", options["view engine"] or "tmpl") + self:conf("view ext", options["view ext"] or "html") + self:conf("view layout", options["view layout"] or "") + self:conf("views", options["views"] or "./app/views/") + + self.locals = {} + self.locals.settings = self.setttings +end + +-- dispatch `req, res` into the pipeline. +function App:handle(req, res, callback) + local router = self.router + local done = callback or function(err) + if err then + if ngx then ngx.log(ngx.ERR, err) end + res:status(500):send("internal error! please check log.") + end + end + + if not router then + return done() + end + + local err_msg + local ok, e = xpcall(function() + router:handle(req, res, done) + end, function(msg) + err_msg = msg + end) + + if not ok then + done(err_msg) + end +end + +function App:use(path, fn) + self:inner_use(3, path, fn) +end + +-- just a mirror for `erroruse` +function App:erruse(path, fn) + self:erroruse(path, fn) +end + +function App:erroruse(path, fn) + self:inner_use(4, path, fn) +end + +-- should be private +function App:inner_use(fn_args_length, path, fn) + local router = self.router + + if path and fn and type(path) == "string" then + router:use(path, fn, fn_args_length) + elseif path and not fn then + fn = path + path = nil + router:use(path, fn, fn_args_length) + else + error("error usage for `middleware`") + end + + return self +end + +function App:init_method() + for http_method, _ in pairs(supported_http_methods) do + self[http_method] = function(_self, path, ...) -- funcs... + _self.router:app_route(http_method, path, ...) + return _self + end + end +end + +function App:all(path, ...) + for http_method, _ in pairs(supported_http_methods) do + self.router:app_route(http_method, path, ...) + end + + return self +end + +function App:conf(setting, val) + self.settings[setting] = val + + if router_conf[setting] == true then + self.router:conf(setting, val) + end + + return self +end + +function App:getconf(setting) + return self.settings[setting] +end + +function App:enable(setting) + self.settings[setting] = true + return self +end + +function App:disable(setting) + self.settings[setting] = false + return self +end + +--- only for dev +function App:gen_graph() + return self.router.trie:gen_graph() +end + +return App diff --git a/website/backend/lor/lib/lor/lib/debug.lua b/website/backend/lor/lib/lor/lib/debug.lua new file mode 100755 index 0000000..77ce51d --- /dev/null +++ b/website/backend/lor/lib/lor/lib/debug.lua @@ -0,0 +1,25 @@ +local pcall = pcall +local type = type +local pairs = pairs + + +local function debug(...) + if not LOR_FRAMEWORK_DEBUG then + return + end + + local info = { ... } + if info and type(info[1]) == 'function' then + pcall(function() info[1]() end) + elseif info and type(info[1]) == 'table' then + for i, v in pairs(info[1]) do + print(i, v) + end + elseif ... ~= nil then + print(...) + else + print("debug not works...") + end +end + +return debug diff --git a/website/backend/lor/lib/lor/lib/holder.lua b/website/backend/lor/lib/lor/lib/holder.lua new file mode 100644 index 0000000..2e6d608 --- /dev/null +++ b/website/backend/lor/lib/lor/lib/holder.lua @@ -0,0 +1,48 @@ +local utils = require("lor.lib.utils.utils") +local ActionHolder = {} + +function ActionHolder:new(func, node, action_type) + local instance = { + id = "action-" .. utils.random(), + node = node, + action_type = action_type, + func = func, + } + + setmetatable(instance, { + __index = self, + __call = self.func + }) + return instance +end + + +local NodeHolder = {} + +function NodeHolder:new() + local instance = { + key = "", + val = nil, -- Node + } + setmetatable(instance, { __index = self }) + return instance +end + +local Matched = {} + +function Matched:new() + local instance = { + node = nil, + params = {}, + pipeline = {}, + } + setmetatable(instance, { __index = self }) + return instance +end + + +return { + ActionHolder = ActionHolder, + NodeHolder = NodeHolder, + Matched = Matched +} diff --git a/website/backend/lor/lib/lor/lib/methods.lua b/website/backend/lor/lib/lor/lib/methods.lua new file mode 100755 index 0000000..b79c5ae --- /dev/null +++ b/website/backend/lor/lib/lor/lib/methods.lua @@ -0,0 +1,15 @@ +-- get and post methods is guaranteed, the others is still in process +-- but all these methods shoule work at most cases by default +local supported_http_methods = { + get = true, -- work well + post = true, -- work well + head = true, -- no test + options = true, -- no test + put = true, -- work well + patch = true, -- no test + delete = true, -- work well + trace = true, -- no test + all = true -- todo: +} + +return supported_http_methods \ No newline at end of file diff --git a/website/backend/lor/lib/lor/lib/middleware/cookie.lua b/website/backend/lor/lib/lor/lib/middleware/cookie.lua new file mode 100755 index 0000000..183807e --- /dev/null +++ b/website/backend/lor/lib/lor/lib/middleware/cookie.lua @@ -0,0 +1,91 @@ +local ck = require("resty.cookie") + +-- Mind: +-- base on 'lua-resty-cookie', https://github.com/cloudflare/lua-resty-cookie +-- this is the default `cookie` middleware +-- you're recommended to define your own `cookie` middleware. + +-- usage example: +-- app:get("/user", function(req, res, next) +-- local ok, err = req.cookie.set({ +-- key = "qq", +-- value = '4==||==hello zhang==||==123456', +-- path = "/", +-- domain = "new.cn", +-- secure = false, --设置后浏览器只有访问https才会把cookie带过来,否则浏览器请求时不带cookie参数 +-- httponly = true, --设置后js 无法读取 +-- --expires = ngx.cookie_time(os.time() + 3600), +-- max_age = 3600, --用秒来设置cookie的生存期。 +-- samesite = "Strict", --或者 Lax 指a域名下收到的cookie 不能通过b域名的表单带过来 +-- extension = "a4334aebaece" +-- }) +-- end) + +local cookie_middleware = function(cookieConfig) + return function(req, res, next) + local COOKIE, err = ck:new() + + if not COOKIE then + req.cookie = {} -- all cookies + res._cookie = nil + else + req.cookie = { + set = function(...) + local _cookie = COOKIE + if not _cookie then + return ngx.log(ngx.ERR, "response#none _cookie found to write") + end + + local p = ... + if type(p) == "table" then + local ok, err = _cookie:set(p) + if not ok then + return ngx.log(ngx.ERR, err) + end + else + local params = { ... } + local ok, err = _cookie:set({ + key = params[1], + value = params[2] or "", + }) + if not ok then + return ngx.log(ngx.ERR, err) + end + end + end, + + get = function (name) + local _cookie = COOKIE + local field, err = _cookie:get(name) + + if not field then + return nil + else + return field + end + end, + + get_all = function () + local _cookie = COOKIE + local fields, err = _cookie:get_all() + + local t = {} + if not fields then + return nil + else + for k, v in pairs(fields) do + if k and v then + t[k] = v + end + end + return t + end + end + } + end + + next() + end +end + +return cookie_middleware diff --git a/website/backend/lor/lib/lor/lib/middleware/init.lua b/website/backend/lor/lib/lor/lib/middleware/init.lua new file mode 100755 index 0000000..ec546d5 --- /dev/null +++ b/website/backend/lor/lib/lor/lib/middleware/init.lua @@ -0,0 +1,9 @@ +local init_middleware = function(req, res, next) + req.res = res + req.next = next + res.req = req + res.locals = res.locals or {} + next() +end + +return init_middleware diff --git a/website/backend/lor/lib/lor/lib/middleware/session.lua b/website/backend/lor/lib/lor/lib/middleware/session.lua new file mode 100755 index 0000000..0a7cc31 --- /dev/null +++ b/website/backend/lor/lib/lor/lib/middleware/session.lua @@ -0,0 +1,184 @@ +local type, xpcall = type, xpcall +local traceback = debug.traceback +local string_sub = string.sub +local string_len = string.len +local http_time = ngx.http_time +local ngx_time = ngx.time +local ck = require("resty.cookie") +local utils = require("lor.lib.utils.utils") +local aes = require("lor.lib.utils.aes") +local base64 = require("lor.lib.utils.base64") + + +local function decode_data(field, aes_key, ase_secret) + if not field or field == "" then return {} end + local payload = base64.decode(field) + local data = {} + local cipher = aes.new() + local decrypt_str = cipher:decrypt(payload, aes_key, ase_secret) + local decode_obj = utils.json_decode(decrypt_str) + return decode_obj or data +end + +local function encode_data(obj, aes_key, ase_secret) + local default = "{}" + local str = utils.json_encode(obj) or default + local cipher = aes.new() + local encrypt_str = cipher:encrypt(str, aes_key, ase_secret) + local encode_encrypt_str = base64.encode(encrypt_str) + return encode_encrypt_str +end + +local function parse_session(field, aes_key, ase_secret) + if not field then return end + return decode_data(field, aes_key, ase_secret) +end + +--- no much secure & performance consideration +--- TODO: optimization & security issues +local session_middleware = function(config) + config = config or {} + config.session_key = config.session_key or "_app_" + if config.refresh_cookie ~= false then + config.refresh_cookie = true + end + if not config.timeout or type(config.timeout) ~= "number" then + config.timeout = 3600 -- default session timeout is 3600 seconds + end + + + local err_tip = "session_aes_key should be set for session middleware" + -- backward compatibility for lor < v0.3.2 + config.session_aes_key = config.session_aes_key or "custom_session_aes_key" + if not config.session_aes_key then + ngx.log(ngx.ERR, err_tip) + end + + local session_key = config.session_key + local session_aes_key = config.session_aes_key + local refresh_cookie = config.refresh_cookie + local timeout = config.timeout + + -- session_aes_secret must be 8 charactors to respect lua-resty-string v0.10+ + local session_aes_secret = config.session_aes_secret or config.secret or "12345678" + if string_len(session_aes_secret) < 8 then + for i=1,8-string_len(session_aes_secret),1 do + session_aes_secret = session_aes_secret .. "0" + end + end + session_aes_secret = string_sub(session_aes_secret, 1, 8) + + ngx.log(ngx.INFO, "session middleware initialized") + return function(req, res, next) + if not session_aes_key then + return next(err_tip) + end + + local cookie, err = ck:new() + if not cookie then + ngx.log(ngx.ERR, "cookie is nil:", err) + end + + local current_session + local session_data, err = cookie:get(session_key) + if err then + ngx.log(ngx.ERR, "cannot get session_data:", err) + else + if session_data then + current_session = parse_session(session_data, session_aes_key, session_aes_secret) + end + end + current_session = current_session or {} + + req.session = { + set = function(...) + local p = ... + if type(p) == "table" then + for i, v in pairs(p) do + current_session[i] = v + end + else + local params = { ... } + if type(params[2]) == "table" then -- set("k", {1, 2, 3}) + current_session[params[1]] = params[2] + else -- set("k", "123") + current_session[params[1]] = params[2] or "" + end + end + + local value = encode_data(current_session, session_aes_key, session_aes_secret) + local expires = http_time(ngx_time() + timeout) + local max_age = timeout + local ok, err = cookie:set({ + key = session_key, + value = value or "", + expires = expires, + max_age = max_age, + path = "/" + }) + + ngx.log(ngx.INFO, "session.set: ", value) + + if err or not ok then + return ngx.log(ngx.ERR, "session.set error:", err) + end + end, + + refresh = function() + if session_data and session_data ~= "" then + local expires = http_time(ngx_time() + timeout) + local max_age = timeout + local ok, err = cookie:set({ + key = session_key, + value = session_data or "", + expires = expires, + max_age = max_age, + path = "/" + }) + if err or not ok then + return ngx.log(ngx.ERR, "session.refresh error:", err) + end + end + end, + + get = function(key) + return current_session[key] + end, + + destroy = function() + local expires = "Thu, 01 Jan 1970 00:00:01 GMT" + local max_age = 0 + local ok, err = cookie:set({ + key = session_key, + value = "", + expires = expires, + max_age = max_age, + path = "/" + }) + if err or not ok then + ngx.log(ngx.ERR, "session.destroy error:", err) + return false + end + + return true + end + } + + if refresh_cookie then + local e, ok + ok = xpcall(function() + req.session.refresh() + end, function() + e = traceback() + end) + + if not ok then + ngx.log(ngx.ERR, "refresh cookie error:", e) + end + end + + next() + end +end + +return session_middleware diff --git a/website/backend/lor/lib/lor/lib/node.lua b/website/backend/lor/lib/lor/lib/node.lua new file mode 100644 index 0000000..efff5ff --- /dev/null +++ b/website/backend/lor/lib/lor/lib/node.lua @@ -0,0 +1,265 @@ +local setmetatable = setmetatable +local type = type +local next = next +local ipairs = ipairs +local table_insert = table.insert +local string_lower = string.lower +local string_format = string.format + +local utils = require("lor.lib.utils.utils") +local supported_http_methods = require("lor.lib.methods") +local ActionHolder = require("lor.lib.holder").ActionHolder +local handler_error_tip = "handler must be `function` that matches `function(req, res, next) ... end`" +local middlware_error_tip = "middlware must be `function` that matches `function(req, res, next) ... end`" +local error_middlware_error_tip = "error middlware must be `function` that matches `function(err, req, res, next) ... end`" +local node_count = 0 + +local function gen_node_id() + local prefix = "node-" + local worker_part = "dw" + if ngx and ngx.worker then + worker_part = ngx.worker.id() + end + node_count = node_count + 1 -- simply count for lua vm level + local unique_part = node_count + local random_part = utils.random() + node_id = prefix .. worker_part .. "-" .. unique_part .. "-" .. random_part + return node_id +end + +local function check_method(method) + if not method then return false end + + method = string_lower(method) + if not supported_http_methods[method] then + return false + end + + return true +end + +local Node = {} + +function Node:new(root) + local is_root = false + if root == true then + is_root = true + end + + local instance = { + id = gen_node_id(), + is_root = is_root, + name = "", + allow = "", + pattern = "", + endpoint = false, + parent = nil, + colon_parent = nil, + children = {}, + colon_child= nil, + handlers = {}, + middlewares = {}, + error_middlewares = {}, + regex = nil + } + setmetatable(instance, { + __index = self, + __tostring = function(s) + local ok, result = pcall(function() + return string_format("name: %s", s.id) + end) + if ok then + return result + else + return "node.tostring() error" + end + end + }) + return instance +end + +function Node:find_child(key) + --print("find_child: ", self.id, self.name, self.children) + for _, c in ipairs(self.children) do + if key == c.key then + return c.val + end + end + return nil +end + +function Node:find_handler(method) + method = string_lower(method) + if not self.handlers or not self.handlers[method] or #self.handlers[method] == 0 then + return false + end + + return true +end + +function Node:use(...) + local middlewares = {...} + if not next(middlewares) then + error("middleware should not be nil or empty") + end + + local empty = true + for _, h in ipairs(middlewares) do + if type(h) == "function" then + local action = ActionHolder:new(h, self, "middleware") + table_insert(self.middlewares, action) + empty = false + elseif type(h) == "table" then + for _, hh in ipairs(h) do + if type(hh) == "function" then + local action = ActionHolder:new(hh, self, "middleware") + table_insert(self.middlewares, action) + empty = false + else + error(middlware_error_tip) + end + end + else + error(middlware_error_tip) + end + end + + if empty then + error("middleware should not be empty") + end + + return self +end + +function Node:error_use(...) + local middlewares = {...} + if not next(middlewares) then + error("error middleware should not be nil or empty") + end + + local empty = true + for _, h in ipairs(middlewares) do + if type(h) == "function" then + local action = ActionHolder:new(h, self, "error_middleware") + table_insert(self.error_middlewares, action) + empty = false + elseif type(h) == "table" then + for _, hh in ipairs(h) do + if type(hh) == "function" then + local action = ActionHolder:new(hh, self, "error_middleware") + table_insert(self.error_middlewares, action) + empty = false + else + error(error_middlware_error_tip) + end + end + else + error(error_middlware_error_tip) + end + end + + if empty then + error("error middleware should not be empty") + end + + return self +end + +function Node:handle(method, ...) + method = string_lower(method) + if not check_method(method) then + error("error method: ", method or "nil") + end + + if self:find_handler(method) then + error("[" .. self.pattern .. "] " .. method .. " handler exists yet!") + end + + if not self.handlers[method] then + self.handlers[method] = {} + end + + local empty = true + local handlers = {...} + if not next(handlers) then + error("handler should not be nil or empty") + end + + for _, h in ipairs(handlers) do + if type(h) == "function" then + local action = ActionHolder:new(h, self, "handler") + table_insert(self.handlers[method], action) + empty = false + elseif type(h) == "table" then + for _, hh in ipairs(h) do + if type(hh) == "function" then + local action = ActionHolder:new(hh, self, "handler") + table_insert(self.handlers[method], action) + empty = false + else + error(handler_error_tip) + end + end + else + error(handler_error_tip) + end + end + + if empty then + error("handler should not be empty") + end + + if self.allow == "" then + self.allow = method + else + self.allow = self.allow .. ", " .. method + end + + return self +end + +function Node:get_allow() + return self.allow +end + +function Node:remove_nested_property(node) + if not node then return end + if node.parent then + node.parent = nil + end + + if node.colon_child then + if node.colon_child.handlers then + for _, h in pairs(node.colon_child.handlers) do + if h then + for _, action in ipairs(h) do + action.func = nil + action.node = nil + end + end + end + end + self:remove_nested_property(node.colon_child) + end + + local children = node.children + if children and #children > 0 then + for _, v in ipairs(children) do + local c = v.val + if c.handlers then -- remove action func + for _, h in pairs(c.handlers) do + if h then + for _, action in ipairs(h) do + action.func = nil + action.node = nil + end + end + end + end + + self:remove_nested_property(v.val) + end + end +end + +return Node diff --git a/website/backend/lor/lib/lor/lib/request.lua b/website/backend/lor/lib/lor/lib/request.lua new file mode 100755 index 0000000..a561cc3 --- /dev/null +++ b/website/backend/lor/lib/lor/lib/request.lua @@ -0,0 +1,75 @@ +local sfind = string.find +local pairs = pairs +local type = type +local setmetatable = setmetatable +local utils = require("lor.lib.utils.utils") + +local Request = {} + +-- new request: init args/params/body etc from http request +function Request:new() + local body = {} -- body params + local headers = ngx.req.get_headers() + + local header = headers['Content-Type'] + -- the post request have Content-Type header set + if header then + if sfind(header, "application/x-www-form-urlencoded", 1, true) then + ngx.req.read_body() + local post_args = ngx.req.get_post_args() + if post_args and type(post_args) == "table" then + for k,v in pairs(post_args) do + body[k] = v + end + end + elseif sfind(header, "application/json", 1, true) then + ngx.req.read_body() + local json_str = ngx.req.get_body_data() + body = utils.json_decode(json_str) + -- form-data request + elseif sfind(header, "multipart", 1, true) then + -- upload request, should not invoke ngx.req.read_body() + -- parsed as raw by default + else + ngx.req.read_body() + body = ngx.req.get_body_data() + end + -- the post request have no Content-Type header set will be parsed as x-www-form-urlencoded by default + else + ngx.req.read_body() + local post_args = ngx.req.get_post_args() + if post_args and type(post_args) == "table" then + for k,v in pairs(post_args) do + body[k] = v + end + end + end + + local instance = { + path = ngx.var.uri, -- uri + method = ngx.req.get_method(), + query = ngx.req.get_uri_args(), + params = {}, + body = body, + body_raw = ngx.req.get_body_data(), + url = ngx.var.request_uri, + origin_uri = ngx.var.request_uri, + uri = ngx.var.request_uri, + headers = headers, -- request headers + + req_args = ngx.var.args, + found = false -- 404 or not + } + setmetatable(instance, { __index = self }) + return instance +end + +function Request:is_found() + return self.found +end + +function Request:set_found(found) + self.found = found +end + +return Request diff --git a/website/backend/lor/lib/lor/lib/response.lua b/website/backend/lor/lib/lor/lib/response.lua new file mode 100755 index 0000000..7409cca --- /dev/null +++ b/website/backend/lor/lib/lor/lib/response.lua @@ -0,0 +1,141 @@ +local pairs = pairs +local type = type +local setmetatable = setmetatable +local tinsert = table.insert +local tconcat = table.concat +local utils = require("lor.lib.utils.utils") + +local Response = {} + +function Response:new() + --ngx.status = 200 + local instance = { + http_status = nil, + headers = {}, + locals = {}, + body = '--default body. you should not see this by default--', + view = nil + } + + setmetatable(instance, { __index = self }) + return instance +end + +-- todo: optimize-compile before used +function Response:render(view_file, data) + if not self.view then + ngx.log(ngx.ERR, "`view` object is nil, maybe you disabled the view engine.") + error("`view` object is nil, maybe you disabled the view engine.") + else + self:set_header('Content-Type', 'text/html; charset=UTF-8') + data = data or {} + data.locals = self.locals -- inject res.locals + + local body = self.view:render(view_file, data) + self:_send(body) + end +end + + +function Response:html(data) + self:set_header('Content-Type', 'text/html; charset=UTF-8') + self:_send(data) +end + +function Response:json(data, empty_table_as_object) + self:set_header('Content-Type', 'application/json; charset=utf-8') + self:_send(utils.json_encode(data, empty_table_as_object)) +end + +function Response:redirect(url, code, query) + if url and not code and not query then -- only one param + ngx.redirect(url) + elseif url and code and not query then -- two param + if type(code) == "number" then + ngx.redirect(url ,code) + elseif type(code) == "table" then + query = code + local q = {} + local is_q_exist = false + if query and type(query) == "table" then + for i,v in pairs(query) do + tinsert(q, i .. "=" .. v) + is_q_exist = true + end + end + + if is_q_exist then + url = url .. "?" .. tconcat(q, "&") + end + + ngx.redirect(url) + else + ngx.redirect(url) + end + else -- three param + local q = {} + local is_q_exist = false + if query and type(query) == "table" then + for i,v in pairs(query) do + tinsert(q, i .. "=" .. v) + is_q_exist = true + end + end + + if is_q_exist then + url = url .. "?" .. tconcat(q, "&") + end + ngx.redirect(url ,code) + end +end + +function Response:location(url, data) + if data and type(data) == "table" then + ngx.req.set_uri_args(data) + ngx.req.set_uri(url, false) + else + ngx.say(url) + ngx.req.set_uri(url, false) + end +end + +function Response:send(text) + self:set_header('Content-Type', 'text/plain; charset=UTF-8') + self:_send(text) +end + +--~============================================================= + +function Response:_send(content) + ngx.status = self.http_status or 200 + ngx.say(content) + DEBUG("=response send content: ", content) +end + +function Response:get_body() + return self.body +end + +function Response:get_headers() + return self.headers +end + +function Response:get_header(key) + return self.headers[key] +end + +function Response:set_body(body) + if body ~= nil then self.body = body end +end + +function Response:status(status) + ngx.status = status + self.http_status = status + return self +end + +function Response:set_header(key, value) + ngx.header[key] = value +end + +return Response diff --git a/website/backend/lor/lib/lor/lib/router/group.lua b/website/backend/lor/lib/lor/lib/router/group.lua new file mode 100644 index 0000000..61a2019 --- /dev/null +++ b/website/backend/lor/lib/lor/lib/router/group.lua @@ -0,0 +1,141 @@ +local setmetatable = setmetatable +local pairs = pairs +local type = type +local error = error +local next = next +local string_format = string.format +local string_lower = string.lower +local table_insert = table.insert +local unpack = table.unpack or unpack + +local supported_http_methods = require("lor.lib.methods") +local debug = require("lor.lib.debug") +local utils = require("lor.lib.utils.utils") +local random = utils.random +local clone = utils.clone +local handler_error_tip = "handler must be `function` that matches `function(req, res, next) ... end`" + +local Group = {} + +function Group:new() + local group = {} + + group.id = random() + group.name = "group-" .. group.id + group.is_group = true + group.apis = {} + self:build_method() + + setmetatable(group, { + __index = self, + __call = self._call, + __tostring = function(s) + return s.name + end + }) + + return group +end + +--- a magick for usage like `lor:Router()` +-- generate a new group for different routes group +function Group:_call() + local cloned = clone(self) + cloned.id = random() + cloned.name = cloned.name .. ":clone-" .. cloned.id + return cloned +end + +function Group:get_apis() + return self.apis +end + +function Group:set_api(path, method, ...) + if not path or not method then + return error("`path` & `method` should not be nil.") + end + + local handlers = {...} + if not next(handlers) then + return error("handler should not be nil or empty") + end + + if type(path) ~= "string" or type(method) ~= "string" or type(handlers) ~= "table" then + return error("params type error.") + end + + local extended_handlers = {} + for _, h in ipairs(handlers) do + if type(h) == "function" then + table_insert(extended_handlers, h) + elseif type(h) == "table" then + for _, hh in ipairs(h) do + if type(hh) == "function" then + table_insert(extended_handlers, hh) + else + error(handler_error_tip) + end + end + else + error(handler_error_tip) + end + end + + method = string_lower(method) + if not supported_http_methods[method] then + return error(string_format("[%s] method is not supported yet.", method)) + end + + self.apis[path] = self.apis[path] or {} + self.apis[path][method] = extended_handlers +end + +function Group:build_method() + for m, _ in pairs(supported_http_methods) do + m = string_lower(m) + + -- 1. group_router:get(func1) + -- 2. group_router:get(func1, func2) + -- 3. group_router:get({func1, func2}) + -- 4. group_router:get(path, func1) + -- 5. group_router:get(path, func1, func2) + -- 6. group_router:get(path, {func1, func2}) + Group[m] = function(myself, ...) + local params = {...} + if not next(params) then return error("params should not be nil or empty") end + + -- case 1 or 3 + if #params == 1 then + if type(params[1]) ~= "function" and type(params[1]) ~= "table" then + return error("it must be an function if there's only one param") + end + + if type(params[1]) == "table" and #(params[1]) == 0 then + return error("params should not be nil or empty") + end + + return Group.set_api(myself, "", m, ...) + end + + -- case 2,4,5,6 + if #params > 1 then + if type(params[1]) == "string" then -- case 4,5,6 + return Group.set_api(myself, params[1], m, unpack(params, 2)) + else -- case 2 + return Group.set_api(myself, "", m, ...) + end + end + + error("error params for group route define") + end + end +end + +function Group:clone() + local cloned = clone(self) + cloned.id = random() + cloned.name = cloned.name .. ":clone-" .. cloned.id + return cloned +end + +return Group diff --git a/website/backend/lor/lib/lor/lib/router/router.lua b/website/backend/lor/lib/lor/lib/router/router.lua new file mode 100755 index 0000000..4c17312 --- /dev/null +++ b/website/backend/lor/lib/lor/lib/router/router.lua @@ -0,0 +1,353 @@ +local pairs = pairs +local ipairs = ipairs +local pcall = pcall +local xpcall = xpcall +local type = type +local error = error +local setmetatable = setmetatable +local traceback = debug.traceback +local tinsert = table.insert +local table_concat = table.concat +local string_format = string.format +local string_lower = string.lower + +local utils = require("lor.lib.utils.utils") +local supported_http_methods = require("lor.lib.methods") +local debug = require("lor.lib.debug") +local Trie = require("lor.lib.trie") +local random = utils.random +local mixin = utils.mixin + +local allowed_conf = { + strict_route = { + t = "boolean" + }, + ignore_case = { + t = "boolean" + }, + max_uri_segments = { + t = "number" + }, + max_fallback_depth = { + t = "number" + }, +} + +local function restore(fn, obj) + local origin = { + path = obj['path'], + query = obj['query'], + next = obj['next'], + locals = obj['locals'], + } + + return function(err) + obj['path'] = origin.path + obj['query'] = origin.query + obj['next'] = origin.next + obj['locals'] = origin.locals + fn(err) + end +end + +local function compose_func(matched, method) + if not matched or type(matched.pipeline) ~= "table" then + return nil + end + + local exact_node = matched.node + local pipeline = matched.pipeline or {} + if not exact_node or not pipeline then + return nil + end + + local stack = {} + for _, p in ipairs(pipeline) do + local middlewares = p.middlewares + local handlers = p.handlers + if middlewares then + for _, middleware in ipairs(middlewares) do + tinsert(stack, middleware) + end + end + + if p.id == exact_node.id and handlers and handlers[method] then + for _, handler in ipairs(handlers[method]) do + tinsert(stack, handler) + end + end + end + + return stack +end + +local function compose_error_handler(node) + if not node then + return nil + end + + local stack = {} + while node do + for _, middleware in ipairs(node.error_middlewares) do + tinsert(stack, middleware) + end + node = node.parent + end + + return stack +end + + +local Router = {} + +function Router:new(options) + local opts = options or {} + local router = {} + + router.name = "router-" .. random() + router.trie = Trie:new({ + ignore_case = opts.ignore_case, + strict_route = opts.strict_route, + max_uri_segments = opts.max_uri_segments, + max_fallback_depth = opts.max_fallback_depth + }) + + self:init() + setmetatable(router, { + __index = self, + __tostring = function(s) + local ok, result = pcall(function() + return string_format("name: %s", s.name) + end) + if ok then + return result + else + return "router.tostring() error" + end + end + }) + + return router +end + +--- a magick to convert `router()` to `router:handle()` +-- so a router() could be regarded as a `middleware` +function Router:call() + return function(req, res, next) + return self:handle(req, res, next) + end +end + +-- dispatch a request +function Router:handle(req, res, out) + local path = req.path + if not path or path == "" then + path = "" + end + local method = req.method and string_lower(req.method) + local done = out + + local stack = nil + local matched = self.trie:match(path) + local matched_node = matched.node + + -- ngx.log(ngx.ERR, "rounter handler req: ", inspect(req)) + + if not method or not matched_node then + if res.status then res:status(404) end + return self:error_handle("404! not found.", req, res, self.trie.root, done) + else + local matched_handlers = matched_node.handlers and matched_node.handlers[method] + if not matched_handlers or #matched_handlers <= 0 then + return self:error_handle("Oh! no handler to process method: " .. method, req, res, self.trie.root, done) + end + + stack = compose_func(matched, method) + if not stack or #stack <= 0 then + return self:error_handle("Oh! no handlers found.", req, res, self.trie.root, done) + end + end + + local stack_len = #stack + req:set_found(true) + local parsed_params = matched.params or {} -- origin params, parsed + req.params = parsed_params + + local idx = 0 + local function next(err) + if err then + return self:error_handle(err, req, res, stack[idx].node, done) + end + + if idx > stack_len then + return done(err) -- err is nil or not + end + + idx = idx + 1 + local handler = stack[idx] + if not handler then + return done(err) + end + + local err_msg + local ok, ee = xpcall(function() + handler.func(req, res, next) + req.params = mixin(parsed_params, req.params) + end, function(msg) + if msg then + if type(msg) == "string" then + err_msg = msg + elseif type(msg) == "table" then + err_msg = "[ERROR]" .. table_concat(msg, "|") .. "[/ERROR]" + end + else + err_msg = "" + end + err_msg = err_msg .. "\n" .. traceback() + end) + + if not ok then + --debug("handler func:call error ---> to error_handle,", ok, "err_msg:", err_msg) + return self:error_handle(err_msg, req, res, handler.node, done) + end + end + + next() +end + +-- dispatch an error +function Router:error_handle(err_msg, req, res, node, done) + local stack = compose_error_handler(node) + if not stack or #stack <= 0 then + return done(err_msg) + end + + local idx = 0 + local stack_len = #stack + local function next(err) + if idx >= stack_len then + return done(err) + end + + idx = idx + 1 + local error_handler = stack[idx] + if not error_handler then + return done(err) + end + + local ok, ee = xpcall(function() + error_handler.func(err, req, res, next) + end, function(msg) + if msg then + if type(msg) == "string" then + err_msg = msg + elseif type(msg) == "table" then + err_msg = "[ERROR]" .. table_concat(msg, "|") .. "[/ERROR]" + end + else + err_msg = "" + end + + err_msg = string_format("%s\n[ERROR in ErrorMiddleware#%s(%s)] %s \n%s", err, idx, error_handler.id, err_msg, traceback()) + end) + + if not ok then + return done(err_msg) + end + end + + next(err_msg) +end + +function Router:use(path, fn, fn_args_length) + if type(fn) == "function" then -- fn is a function + local node + if not path then + node = self.trie.root + else + node = self.trie:add_node(path) + end + if fn_args_length == 3 then + node:use(fn) + elseif fn_args_length == 4 then + node:error_use(fn) + end + elseif fn and fn.is_group == true then -- fn is a group router + if fn_args_length ~= 3 then + error("illegal param, fn_args_length should be 3") + end + + path = path or "" -- if path is nil, then mount it on `root` + self:merge_group(path, fn) + end + + return self +end + +function Router:merge_group(prefix, group) + local apis = group:get_apis() + + if apis then + for uri, api_methods in pairs(apis) do + if type(api_methods) == "table" then + local path + if uri == "" then -- for group index route + path = utils.clear_slash(prefix) + else + path = utils.clear_slash(prefix .. "/" .. uri) + end + + local node = self.trie:add_node(path) + if not node then + return error("cann't define node on router trie, path:" .. path) + end + + for method, handlers in pairs(api_methods) do + local m = string_lower(method) + if supported_http_methods[m] == true then + node:handle(m, handlers) + end -- supported method + end + end + end + end -- ugly arrow style for missing `continue` + + return self +end + +function Router:app_route(http_method, path, ...) + local node = self.trie:add_node(path) + node:handle(http_method, ...) + return self +end + +function Router:init() + for http_method, _ in pairs(supported_http_methods) do + self[http_method] = function(s, path, ...) + local node = s.trie:add_node(path) + node:handle(http_method, ...) + return s + end + end +end + +function Router:conf(setting, val) + local allow = allowed_conf[setting] + if allow then + if allow.t == "boolean" then + + if val == "true" or val == true then + self.trie[setting] = true + elseif val == "false" or val == false then + self.trie[setting] = false + end + elseif allow.t == "number" then + val = tonumber(val) + self.trie[setting] = val or self[setting] + end + end + + return self +end + +return Router diff --git a/website/backend/lor/lib/lor/lib/trie.lua b/website/backend/lor/lib/lor/lib/trie.lua new file mode 100644 index 0000000..ff79a3e --- /dev/null +++ b/website/backend/lor/lib/lor/lib/trie.lua @@ -0,0 +1,532 @@ +local setmetatable = setmetatable +local tonumber = tonumber +local string_lower = string.lower +local string_find = string.find +local string_sub = string.sub +local string_gsub = string.gsub +local string_len = string.len +local string_format = string.format +local table_insert = table.insert +local table_remove = table.remove +local table_concat = table.concat + +local utils = require("lor.lib.utils.utils") +local holder = require("lor.lib.holder") +local Node = require("lor.lib.node") +local NodeHolder = holder.NodeHolder +local Matched = holder.Matched +local mixin = utils.mixin +local valid_segment_tip = "valid path should only contains: [A-Za-z0-9._%-~]" + + +local function check_segment(segment) + local tmp = string_gsub(segment, "([A-Za-z0-9._%-~]+)", "") + if tmp ~= "" then + return false + end + return true +end + +local function check_colon_child(node, colon_child) + if not node or not colon_child then + return false, nil + end + + if node.name ~= colon_child.name or node.regex ~= colon_child.regex then + return false, colon_child + end + + return true, nil -- could be added +end + +local function get_or_new_node(parent, frag, ignore_case) + if not frag or frag == "/" or frag == "" then + frag = "" + end + + if ignore_case == true then + frag = string_lower(frag) + end + + local node = parent:find_child(frag) + if node then + return node + end + + node = Node:new() + node.parent = parent + + if frag == "" then + local nodePack = NodeHolder:new() + nodePack.key = frag + nodePack.val = node + table_insert(parent.children, nodePack) + else + local first = string_sub(frag, 1, 1) + if first == ":" then + local name = string_sub(frag, 2) + local trailing = string_sub(name, -1) + + if trailing == ')' then + local index = string_find(name, "%(") + if index and index > 1 then + local regex = string_sub(name, index+1, #name-1) + if #regex > 0 then + name = string_sub(name, 1, index-1 ) + node.regex = regex + else + error("invalid pattern[1]: " .. frag) + end + end + end + + local is_name_valid = check_segment(name) + if not is_name_valid then + error("invalid pattern[2], illegal path:" .. name .. ", " .. valid_segment_tip) + end + node.name = name + + local colon_child = parent.colon_child + if colon_child then + local valid, conflict = check_colon_child(node, colon_child) + if not valid then + error("invalid pattern[3]: [" .. name .. "] conflict with [" .. conflict.name .. "]") + else + return colon_child + end + end + + parent.colon_child = node + else + local is_name_valid = check_segment(frag) + if not is_name_valid then + error("invalid pattern[6]: " .. frag .. ", " .. valid_segment_tip) + end + + local nodePack = NodeHolder:new() + nodePack.key = frag + nodePack.val = node + table_insert(parent.children, nodePack) + end + end + + return node +end + +local function insert_node(parent, frags, ignore_case) + local frag = frags[1] + local child = get_or_new_node(parent, frag, ignore_case) + + if #frags >= 1 then + table_remove(frags, 1) + end + + if #frags == 0 then + child.endpoint = true + return child + end + + return insert_node(child, frags, ignore_case) +end + +local function get_pipeline(node) + local pipeline = {} + if not node then return pipeline end + + local tmp = {} + local origin_node = node + table_insert(tmp, origin_node) + while node.parent + do + table_insert(tmp, node.parent) + node = node.parent + end + + for i = #tmp, 1, -1 do + table_insert(pipeline, tmp[i]) + end + + return pipeline +end + + +local Trie = {} + +function Trie:new(opts) + opts = opts or {} + local trie = { + -- limit to avoid dead `while` or attack for fallback lookup + max_fallback_depth = 100, + + -- limit to avoid uri attack. e.g. a long uri, /a/b/c/d/e/f/g/h/i/j/k... + max_uri_segments = 100, + + -- should ignore case or not + ignore_case = true, + + -- [true]: "test.com/" is not the same with "test.com". + -- [false]: "test.com/" will match "test.com/" first, then try to math "test.com" if not exists + strict_route = true, + + -- the root node of this trie structure + root = Node:new(true) + } + + trie.max_fallback_depth = tonumber(opts.max_fallback_depth) or trie.max_fallback_depth + trie.max_uri_segments = tonumber(opts.max_uri_segments) or trie.max_uri_segments + trie.ignore_case = opts.ignore_case or trie.ignore_case + trie.strict_route = not (opts.strict_route == false) + + setmetatable(trie, { + __index = self, + __tostring = function(s) + return string_format("Trie, ignore_case:%s strict_route:%s max_uri_segments:%d max_fallback_depth:%d", + s.ignore_case, s.strict_route, s.max_uri_segments, s.max_fallback_depth) + end + }) + + return trie +end + +function Trie:add_node(pattern) + pattern = utils.trim_path_spaces(pattern) + + if string_find(pattern, "//") then + error("`//` is not allowed: " .. pattern) + end + + local tmp_pattern = utils.trim_prefix_slash(pattern) + local tmp_segments = utils.split(tmp_pattern, "/") + + local node = insert_node(self.root, tmp_segments, self.ignore_case) + if node.pattern == "" then + node.pattern = pattern + end + + return node +end + +--- get matched colon node +function Trie:get_colon_node(parent, segment) + local child = parent.colon_child + if child and child.regex and not utils.is_match(segment, child.regex) then + child = nil -- illegal & not mathed regrex + end + return child +end + +--- retry to fallback to lookup the colon nodes in `stack` +function Trie:fallback_lookup(fallback_stack, segments, params) + if #fallback_stack == 0 then + return false + end + + local fallback = table_remove(fallback_stack, #fallback_stack) + local segment_index = fallback.segment_index + local parent = fallback.colon_node + local matched = Matched:new() + + if parent.name ~= "" then -- fallback to the colon node and fill param if matched + matched.params[parent.name] = segments[segment_index] + end + mixin(params, matched.params) -- mixin params parsed before + + local flag = true + for i, s in ipairs(segments) do + if i <= segment_index then -- mind: should use <= not < + -- continue + else + local node, colon_node, is_same = self:find_matched_child(parent, s) + if self.ignore_case and node == nil then + node, colon_node, is_same = self:find_matched_child(parent, string_lower(s)) + end + + if colon_node and not is_same then + -- save colon node to fallback stack + table_insert(fallback_stack, { + segment_index = i, + colon_node = colon_node + }) + end + + if node == nil then -- both exact child and colon child is nil + flag = false -- should not set parent value + break + end + + parent = node + end + end + + if flag and parent.endpoint then + matched.node = parent + matched.pipeline = get_pipeline(parent) + end + + if matched.node then + return matched + else + return false + end +end + +--- find exactly mathed node and colon node +function Trie:find_matched_child(parent, segment) + local child = parent:find_child(segment) + local colon_node = self:get_colon_node(parent, segment) + + if child then + if colon_node then + return child, colon_node, false + else + return child, nil, false + end + else -- not child + if colon_node then + return colon_node, colon_node, true -- 后续不再压栈 + else + return nil, nil, false + end + end +end + +function Trie:match(path) + if not path or path == "" then + error("`path` should not be nil or empty") + end + + path = utils.slim_path(path) + + local first = string_sub(path, 1, 1) + if first ~= '/' then + error("`path` is not start with prefix /: " .. path) + end + + if path == "" then -- special case: regard "test.com" as "test.com/" + path = "/" + end + + local matched = self:_match(path) + if not matched.node and self.strict_route ~= true then + if string_sub(path, -1) == '/' then -- retry to find path without last slash + matched = self:_match(string_sub(path, 1, -2)) + end + end + + return matched +end + +function Trie:_match(path) + local start_pos = 2 + local end_pos = string_len(path) + 1 + local segments = {} + for i = 2, end_pos, 1 do -- should set max depth to avoid attack + if i < end_pos and string_sub(path, i, i) ~= '/' then + -- continue + else + local segment = string_sub(path, start_pos, i-1) + table_insert(segments, segment) + start_pos = i + 1 + end + end + + local flag = true -- whether to continue to find matched node or not + local matched = Matched:new() + local parent = self.root + local fallback_stack = {} + for i, s in ipairs(segments) do + local node, colon_node, is_same = self:find_matched_child(parent, s) + if self.ignore_case and node == nil then + node, colon_node, is_same = self:find_matched_child(parent, string_lower(s)) + end + + if colon_node and not is_same then + table_insert(fallback_stack, { + segment_index = i, + colon_node = colon_node + }) + end + + if node == nil then -- both exact child and colon child is nil + flag = false -- should not set parent value + break + end + + parent = node + if parent.name ~= "" then + matched.params[parent.name] = s + end + end + + if flag and parent.endpoint then + matched.node = parent + end + + local params = matched.params or {} + if not matched.node then + local depth = 0 + local exit = false + + while not exit do + depth = depth + 1 + if depth > self.max_fallback_depth then + error("fallback lookup reaches the limit: " .. self.max_fallback_depth) + end + + exit = self:fallback_lookup(fallback_stack, segments, params) + if exit then + matched = exit + break + end + + if #fallback_stack == 0 then + break + end + end + end + + matched.params = params + if matched.node then + matched.pipeline = get_pipeline(matched.node) + end + + return matched +end + +--- only for dev purpose: pretty json preview +-- must not be invoked in runtime +function Trie:remove_nested_property(node) + if not node then return end + if node.parent then + node.parent = nil + end + if node.handlers then + for _, h in pairs(node.handlers) do + if h then + for _, action in ipairs(h) do + action.func = nil + action.node = nil + end + end + end + end + if node.middlewares then + for _, m in pairs(node.middlewares) do + if m then + m.func = nil + m.node = nil + end + end + end + if node.error_middlewares then + for _, m in pairs(node.error_middlewares) do + if m then + m.func = nil + m.node = nil + end + end + end + + if node.colon_child then + if node.colon_child.handlers then + for _, h in pairs(node.colon_child.handlers) do + if h then + for _, action in ipairs(h) do + action.func = nil + action.node = nil + end + end + end + end + if node.colon_child.middlewares then + for _, m in pairs(node.colon_child.middlewares) do + if m then + m.func = nil + m.node = nil + end + end + end + if node.colon_child.error_middlewares then + for _, m in pairs(node.colon_child.error_middlewares) do + if m then + m.func = nil + m.node = nil + end + end + end + self:remove_nested_property(node.colon_child) + end + + local children = node.children + if children and #children > 0 then + for _, v in ipairs(children) do + local c = v.val + if c.handlers then -- remove action func + for _, h in pairs(c.handlers) do + if h then + for _, action in ipairs(h) do + action.func = nil + action.node = nil + end + end + end + end + if c.middlewares then + for _, m in pairs(c.middlewares) do + if m then + m.func = nil + m.node = nil + end + end + end + if c.error_middlewares then + for _, m in pairs(c.error_middlewares) do + if m then + m.func = nil + m.node = nil + end + end + end + + self:remove_nested_property(v.val) + end + end +end + +--- only for dev purpose: graph preview +-- must not be invoked in runtime +function Trie:gen_graph() + local cloned_trie = utils.clone(self) + cloned_trie:remove_nested_property(cloned_trie.root) + local result = {"graph TD", cloned_trie.root.id .. "((root))"} + + local function recursive_draw(node, res) + if node.is_root then node.key = "root" end + + local colon_child = node.colon_child + if colon_child then + table_insert(res, node.id .. "-->" .. colon_child.id .. "(:" .. colon_child.name .. "
" .. colon_child.id .. ")") + recursive_draw(colon_child, res) + end + + local children = node.children + if children and #children > 0 then + for _, v in ipairs(children) do + if v.key == "" then + --table_insert(res, node.id .. "-->" .. v.val.id .. "[*EMPTY*]") + local text = {node.id, "-->", v.val.id, "(
", "*EMPTY*", "
", v.val.id, "
)"} + table_insert(res, table_concat(text, "")) + else + local text = {node.id, "-->", v.val.id, "(
", v.key, "
", v.val.id, "
)"} + table_insert(res, table_concat(text, "")) + end + recursive_draw(v.val, res) + end + end + end + + recursive_draw(cloned_trie.root, result) + return table.concat(result, "\n") +end + +return Trie diff --git a/website/backend/lor/lib/lor/lib/utils/aes.lua b/website/backend/lor/lib/lor/lib/utils/aes.lua new file mode 100644 index 0000000..98456a1 --- /dev/null +++ b/website/backend/lor/lib/lor/lib/utils/aes.lua @@ -0,0 +1,54 @@ +-- from lua-resty-session +local setmetatable = setmetatable +local tonumber = tonumber +local aes = require "resty.aes" +local cip = aes.cipher +local hashes = aes.hash +local var = ngx.var + +local CIPHER_MODES = { + ecb = "ecb", + cbc = "cbc", + cfb1 = "cfb1", + cfb8 = "cfb8", + cfb128 = "cfb128", + ofb = "ofb", + ctr = "ctr" +} + +local CIPHER_SIZES = { + ["128"] = 128, + ["192"] = 192, + ["256"] = 256 +} + +local defaults = { + size = CIPHER_SIZES[var.session_aes_size] or 256, + mode = CIPHER_MODES[var.session_aes_mode] or "cbc", + hash = hashes[var.session_aes_hash] or "sha512", + rounds = tonumber(var.session_aes_rounds) or 1 +} + +local cipher = {} + +cipher.__index = cipher + +function cipher.new(config) + local a = config and config.aes or defaults + return setmetatable({ + size = CIPHER_SIZES[a.size or defaults.size] or 256, + mode = CIPHER_MODES[a.mode or defaults.mode] or "cbc", + hash = hashes[a.hash or defaults.hash] or hashes.sha512, + rounds = tonumber(a.rounds or defaults.rounds) or 1 + }, cipher) +end + +function cipher:encrypt(d, k, s) + return aes:new(k, s, cip(self.size, self.mode), self.hash, self.rounds):encrypt(d) +end + +function cipher:decrypt(d, k, s) + return aes:new(k, s, cip(self.size, self.mode), self.hash, self.rounds):decrypt(d) +end + +return cipher diff --git a/website/backend/lor/lib/lor/lib/utils/base64.lua b/website/backend/lor/lib/lor/lib/utils/base64.lua new file mode 100644 index 0000000..d1872aa --- /dev/null +++ b/website/backend/lor/lib/lor/lib/utils/base64.lua @@ -0,0 +1,27 @@ +local ngx = ngx +local base64enc = ngx.encode_base64 +local base64dec = ngx.decode_base64 + +local ENCODE_CHARS = { + ["+"] = "-", + ["/"] = "_", + ["="] = "." +} + +local DECODE_CHARS = { + ["-"] = "+", + ["_"] = "/", + ["."] = "=" +} + +local base64 = {} + +function base64.encode(value) + return (base64enc(value):gsub("[+/=]", ENCODE_CHARS)) +end + +function base64.decode(value) + return base64dec((value:gsub("[-_.]", DECODE_CHARS))) +end + +return base64 diff --git a/website/backend/lor/lib/lor/lib/utils/utils.lua b/website/backend/lor/lib/lor/lib/utils/utils.lua new file mode 100755 index 0000000..52ca1d5 --- /dev/null +++ b/website/backend/lor/lib/lor/lib/utils/utils.lua @@ -0,0 +1,152 @@ +local type = type +local pairs = pairs +local setmetatable = setmetatable +local mrandom = math.random +local sreverse = string.reverse +local sfind = string.find +local sgsub = string.gsub +local smatch = string.match +local table_insert = table.insert +local json = require("cjson") + +local _M = {} + +function _M.clone(o) + local lookup_table = {} + local function _copy(object) + if type(object) ~= "table" then + return object + elseif lookup_table[object] then + return lookup_table[object] + end + local new_object = {} + lookup_table[object] = new_object + for key, value in pairs(object) do + new_object[_copy(key)] = _copy(value) + end + return setmetatable(new_object, getmetatable(object)) + end + return _copy(o) +end + +function _M.clear_slash(s) + local r, _ = sgsub(s, "(/+)", "/") + return r +end + +function _M.is_table_empty(t) + if t == nil or _G.next(t) == nil then + return true + else + return false + end +end + +function _M.table_is_array(t) + if type(t) ~= "table" then return false end + local i = 0 + for _ in pairs(t) do + i = i + 1 + if t[i] == nil then return false end + end + return true +end + +function _M.mixin(a, b) + if a and b then + for k, _ in pairs(b) do + a[k] = b[k] + end + end + return a +end + +function _M.random() + return mrandom(0, 10000) +end + +function _M.json_encode(data, empty_table_as_object) + local json_value + if json.encode_empty_table_as_object then + -- empty table encoded as array default + json.encode_empty_table_as_object(empty_table_as_object or false) + end + if require("ffi").os ~= "Windows" then + json.encode_sparse_array(true) + end + pcall(function(d) json_value = json.encode(d) end, data) + return json_value +end + +function _M.json_decode(str) + local ok, data = pcall(json.decode, str) + if ok then + return data + end +end + +function _M.start_with(str, substr) + if str == nil or substr == nil then + return false + end + if sfind(str, substr) ~= 1 then + return false + else + return true + end +end + +function _M.end_with(str, substr) + if str == nil or substr == nil then + return false + end + local str_reverse = sreverse(str) + local substr_reverse = sreverse(substr) + if sfind(str_reverse, substr_reverse) ~= 1 then + return false + else + return true + end +end + +function _M.is_match(uri, pattern) + if not pattern then + return false + end + + local ok = smatch(uri, pattern) + if ok then return true else return false end +end + +function _M.trim_prefix_slash(s) + local str, _ = sgsub(s, "^(//*)", "") + return str +end + +function _M.trim_suffix_slash(s) + local str, _ = sgsub(s, "(//*)$", "") + return str +end + +function _M.trim_path_spaces(path) + if not path or path == "" then return path end + return sgsub(path, "( *)", "") +end + +function _M.slim_path(path) + if not path or path == "" then return path end + return sgsub(path, "(//*)", "/") +end + +function _M.split(str, delimiter) + if not str or str == "" then return {} end + if not delimiter or delimiter == "" then return { str } end + + local result = {} + for match in (str .. delimiter):gmatch("(.-)" .. delimiter) do + table_insert(result, match) + end + return result +end + +return _M diff --git a/website/backend/lor/lib/lor/lib/view.lua b/website/backend/lor/lib/lor/lib/view.lua new file mode 100755 index 0000000..0090c4f --- /dev/null +++ b/website/backend/lor/lib/lor/lib/view.lua @@ -0,0 +1,54 @@ +local pairs = pairs +local type = type +local setmetatable = setmetatable +local tostring = tostring +local template = require "resty.template" +local template_new = template.new + +local View = {} + +function View:new(view_config) + local instance = {} + instance.view_enable = view_config.view_enable + if instance.view_enable then + if ngx.var.template_root then + ngx.var.template_root = view_config.views + else + ngx.log(ngx.ERR, "$template_root is not set in nginx.conf") + end + end + instance.view_engine = view_config.view_engine + instance.view_ext = view_config.view_ext + instance.view_layout = view_config.view_layout + instance.views = view_config.views + + setmetatable(instance, {__index = self}) + return instance +end + +function View:caching() +end + +-- to optimize +function View:render(view_file, data) + if not self.view_enable then + ngx.log(ngx.ERR, "view is not enabled. you may need `app:conf('view enable', true)`") + else + local view_file_name = view_file .. "." .. self.view_ext + local layout_file_name = self.view_layout .. "." .. self.view_ext + + local t = template_new(view_file_name) + if self.view_layout ~= "" then + t = template_new(view_file_name,layout_file_name) + end + if data and type(data) == 'table' then + for k,v in pairs(data) do + t[k] = v + end + end + + return tostring(t) + end +end + +return View \ No newline at end of file diff --git a/website/backend/lor/lib/lor/lib/wrap.lua b/website/backend/lor/lib/lor/lib/wrap.lua new file mode 100755 index 0000000..255893d --- /dev/null +++ b/website/backend/lor/lib/lor/lib/wrap.lua @@ -0,0 +1,40 @@ +local setmetatable = setmetatable + +local _M = {} + +function _M:new(create_app, Router, Group, Request, Response) + local instance = {} + instance.router = Router + instance.group = Group + instance.request = Request + instance.response = Response + instance.fn = create_app + instance.app = nil + + setmetatable(instance, { + __index = self, + __call = self.create_app + }) + + return instance +end + +-- Generally, this should only be used by `lor` framework itself. +function _M:create_app(options) + self.app = self.fn(options) + return self.app +end + +function _M:Router(options) + return self.group:new(options) +end + +function _M:Request() + return self.request:new() +end + +function _M:Response() + return self.response:new() +end + +return _M diff --git a/website/backend/lor/lib/lor/version.lua b/website/backend/lor/lib/lor/version.lua new file mode 100755 index 0000000..b8cbfa0 --- /dev/null +++ b/website/backend/lor/lib/lor/version.lua @@ -0,0 +1 @@ +return "0.3.4" diff --git a/website/backend/lor/resty/cookie.lua b/website/backend/lor/resty/cookie.lua new file mode 100755 index 0000000..cb5412f --- /dev/null +++ b/website/backend/lor/resty/cookie.lua @@ -0,0 +1,192 @@ +-- https://github.com/cloudflare/lua-resty-cookie/blob/master/lib/resty/cookie.lua + +-- Copyright (C) 2013 Jiale Zhi (calio), Cloudflare Inc. +-- See RFC6265 http://tools.ietf.org/search/rfc6265 +-- require "luacov" + +local type = type +local byte = string.byte +local sub = string.sub +local format = string.format +local log = ngx.log +local ERR = ngx.ERR +local ngx_header = ngx.header + +local EQUAL = byte("=") +local SEMICOLON = byte(";") +local SPACE = byte(" ") +local HTAB = byte("\t") + +-- table.new(narr, nrec) +local ok, new_tab = pcall(require, "table.new") +if not ok then + new_tab = function () return {} end +end + +local ok, clear_tab = pcall(require, "table.clear") +if not ok then + clear_tab = function(tab) for k, _ in pairs(tab) do tab[k] = nil end end +end + +local _M = new_tab(0, 2) + +_M._VERSION = '0.01' + + +local function get_cookie_table(text_cookie) + if type(text_cookie) ~= "string" then + log(ERR, format("expect text_cookie to be \"string\" but found %s", + type(text_cookie))) + return {} + end + + local EXPECT_KEY = 1 + local EXPECT_VALUE = 2 + local EXPECT_SP = 3 + + local n = 0 + local len = #text_cookie + + for i=1, len do + if byte(text_cookie, i) == SEMICOLON then + n = n + 1 + end + end + + local cookie_table = new_tab(0, n + 1) + + local state = EXPECT_SP + local i = 1 + local j = 1 + local key, value + + while j <= len do + if state == EXPECT_KEY then + if byte(text_cookie, j) == EQUAL then + key = sub(text_cookie, i, j - 1) + state = EXPECT_VALUE + i = j + 1 + end + elseif state == EXPECT_VALUE then + if byte(text_cookie, j) == SEMICOLON + or byte(text_cookie, j) == SPACE + or byte(text_cookie, j) == HTAB + then + value = sub(text_cookie, i, j - 1) + cookie_table[key] = value + + key, value = nil, nil + state = EXPECT_SP + i = j + 1 + end + elseif state == EXPECT_SP then + if byte(text_cookie, j) ~= SPACE + and byte(text_cookie, j) ~= HTAB + then + state = EXPECT_KEY + i = j + j = j - 1 + end + end + j = j + 1 + end + + if key ~= nil and value == nil then + cookie_table[key] = sub(text_cookie, i) + end + + return cookie_table +end + +function _M.new(self) + local _cookie = ngx.var.http_cookie + --if not _cookie then + --return nil, "no cookie found in current request" + --end + return setmetatable({ _cookie = _cookie, set_cookie_table = new_tab(4, 0) }, + { __index = self }) +end + +function _M.get(self, key) + if not self._cookie then + return nil, "no cookie found in the current request" + end + if self.cookie_table == nil then + self.cookie_table = get_cookie_table(self._cookie) + end + + return self.cookie_table[key] +end + +function _M.get_all(self) + if not self._cookie then + return nil, "no cookie found in the current request" + end + + if self.cookie_table == nil then + self.cookie_table = get_cookie_table(self._cookie) + end + + return self.cookie_table +end + +local function bake(cookie) + if not cookie.key or not cookie.value then + return nil, 'missing cookie field "key" or "value"' + end + + if cookie["max-age"] then + cookie.max_age = cookie["max-age"] + end + local str = cookie.key .. "=" .. cookie.value + .. (cookie.expires and "; Expires=" .. cookie.expires or "") + .. (cookie.max_age and "; Max-Age=" .. cookie.max_age or "") + .. (cookie.domain and "; Domain=" .. cookie.domain or "") + .. (cookie.path and "; Path=" .. cookie.path or "") + .. (cookie.secure and "; Secure" or "") + .. (cookie.httponly and "; HttpOnly" or "") + .. (cookie.extension and "; " .. cookie.extension or "") + return str +end + +function _M.set(self, cookie) + local cookie_str, err = bake(cookie) + if not cookie_str then + return nil, err + end + + local set_cookie = ngx_header['Set-Cookie'] + local set_cookie_type = type(set_cookie) + local t = self.set_cookie_table + clear_tab(t) + + if set_cookie_type == "string" then + -- only one cookie has been setted + if set_cookie ~= cookie_str then + t[1] = set_cookie + t[2] = cookie_str + ngx_header['Set-Cookie'] = t + end + elseif set_cookie_type == "table" then + -- more than one cookies has been setted + local size = #set_cookie + + -- we can not set cookie like ngx.header['Set-Cookie'][3] = val + -- so create a new table, copy all the values, and then set it back + for i=1, size do + t[i] = ngx_header['Set-Cookie'][i] + if t[i] == cookie_str then + -- new cookie is duplicated + return true + end + end + t[size + 1] = cookie_str + ngx_header['Set-Cookie'] = t + else + -- no cookie has been setted + ngx_header['Set-Cookie'] = cookie_str + end + return true +end + +return _M \ No newline at end of file diff --git a/website/backend/lor/resty/template.lua b/website/backend/lor/resty/template.lua new file mode 100755 index 0000000..0de71bd --- /dev/null +++ b/website/backend/lor/resty/template.lua @@ -0,0 +1,478 @@ +local setmetatable = setmetatable +local loadstring = loadstring +local loadchunk +local tostring = tostring +local setfenv = setfenv +local require = require +local capture +local concat = table.concat +local assert = assert +local prefix +local write = io.write +local pcall = pcall +local phase +local open = io.open +local load = load +local type = type +local dump = string.dump +local find = string.find +local gsub = string.gsub +local byte = string.byte +local null +local sub = string.sub +local ngx = ngx +local jit = jit +local var + +local _VERSION = _VERSION +local _ENV = _ENV +local _G = _G + +local HTML_ENTITIES = { + ["&"] = "&", + ["<"] = "<", + [">"] = ">", + ['"'] = """, + ["'"] = "'", + ["/"] = "/" +} + +local CODE_ENTITIES = { + ["{"] = "{", + ["}"] = "}", + ["&"] = "&", + ["<"] = "<", + [">"] = ">", + ['"'] = """, + ["'"] = "'", + ["/"] = "/" +} + +local VAR_PHASES + +local ok, newtab = pcall(require, "table.new") +if not ok then newtab = function() return {} end end + +local caching = true +local template = newtab(0, 12) + +template._VERSION = "1.9" +template.cache = {} + +local function enabled(val) + if val == nil then return true end + return val == true or (val == "1" or val == "true" or val == "on") +end + +local function trim(s) + return gsub(gsub(s, "^%s+", ""), "%s+$", "") +end + +local function rpos(view, s) + while s > 0 do + local c = sub(view, s, s) + if c == " " or c == "\t" or c == "\0" or c == "\x0B" then + s = s - 1 + else + break + end + end + return s +end + +local function escaped(view, s) + if s > 1 and sub(view, s - 1, s - 1) == "\\" then + if s > 2 and sub(view, s - 2, s - 2) == "\\" then + return false, 1 + else + return true, 1 + end + end + return false, 0 +end + +local function readfile(path) + local file = open(path, "rb") + if not file then return nil end + local content = file:read "*a" + file:close() + return content +end + +local function loadlua(path) + return readfile(path) or path +end + +local function loadngx(path) + local vars = VAR_PHASES[phase()] + local file, location = path, vars and var.template_location + if sub(file, 1) == "/" then file = sub(file, 2) end + if location and location ~= "" then + if sub(location, -1) == "/" then location = sub(location, 1, -2) end + local res = capture(concat{ location, '/', file}) + if res.status == 200 then return res.body end + end + local root = vars and (var.template_root or var.document_root) or prefix + if sub(root, -1) == "/" then root = sub(root, 1, -2) end + return readfile(concat{ root, "/", file }) or path +end + +do + if ngx then + VAR_PHASES = { + set = true, + rewrite = true, + access = true, + content = true, + header_filter = true, + body_filter = true, + log = true + } + template.print = ngx.print or write + template.load = loadngx + prefix, var, capture, null, phase = ngx.config.prefix(), ngx.var, ngx.location.capture, ngx.null, ngx.get_phase + if VAR_PHASES[phase()] then + caching = enabled(var.template_cache) + end + else + template.print = write + template.load = loadlua + end + if _VERSION == "Lua 5.1" then + local context = { __index = function(t, k) + return t.context[k] or t.template[k] or _G[k] + end } + if jit then + loadchunk = function(view) + return assert(load(view, nil, nil, setmetatable({ template = template }, context))) + end + else + loadchunk = function(view) + local func = assert(loadstring(view)) + setfenv(func, setmetatable({ template = template }, context)) + return func + end + end + else + local context = { __index = function(t, k) + return t.context[k] or t.template[k] or _ENV[k] + end } + loadchunk = function(view) + return assert(load(view, nil, nil, setmetatable({ template = template }, context))) + end + end +end + +function template.caching(enable) + if enable ~= nil then caching = enable == true end + return caching +end + +function template.output(s) + if s == nil or s == null then return "" end + if type(s) == "function" then return template.output(s()) end + return tostring(s) +end + +function template.escape(s, c) + if type(s) == "string" then + if c then return gsub(s, "[}{\">/<'&]", CODE_ENTITIES) end + return gsub(s, "[\">/<'&]", HTML_ENTITIES) + end + return template.output(s) +end + +function template.new(view, layout) + assert(view, "view was not provided for template.new(view, layout).") + local render, compile = template.render, template.compile + if layout then + if type(layout) == "table" then + return setmetatable({ render = function(self, context) + local context = context or self + context.blocks = context.blocks or {} + context.view = compile(view)(context) + layout.blocks = context.blocks or {} + layout.view = context.view or "" + return layout:render() + end }, { __tostring = function(self) + local context = self + context.blocks = context.blocks or {} + context.view = compile(view)(context) + layout.blocks = context.blocks or {} + layout.view = context.view + return tostring(layout) + end }) + else + return setmetatable({ render = function(self, context) + local context = context or self + context.blocks = context.blocks or {} + context.view = compile(view)(context) + return render(layout, context) + end }, { __tostring = function(self) + local context = self + context.blocks = context.blocks or {} + context.view = compile(view)(context) + return compile(layout)(context) + end }) + end + end + return setmetatable({ render = function(self, context) + return render(view, context or self) + end }, { __tostring = function(self) + return compile(view)(self) + end }) +end + +function template.precompile(view, path, strip) + local chunk = dump(template.compile(view), strip ~= false) + if path then + local file = open(path, "wb") + file:write(chunk) + file:close() + end + return chunk +end + +function template.compile(view, key, plain) + assert(view, "view was not provided for template.compile(view, key, plain).") + if key == "no-cache" then + return loadchunk(template.parse(view, plain)), false + end + key = key or view + local cache = template.cache + if cache[key] then return cache[key], true end + local func = loadchunk(template.parse(view, plain)) + if caching then cache[key] = func end + return func, false +end + +function template.parse(view, plain) + assert(view, "view was not provided for template.parse(view, plain).") + if not plain then + view = template.load(view) + if byte(view, 1, 1) == 27 then return view end + end + local j = 2 + local c = {[[ +context=... or {} +local function include(v, c) return template.compile(v)(c or context) end +local ___,blocks,layout={},blocks or {} +]] } + local i, s = 1, find(view, "{", 1, true) + while s do + local t, p = sub(view, s + 1, s + 1), s + 2 + if t == "{" then + local e = find(view, "}}", p, true) + if e then + local z, w = escaped(view, s) + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, s - 1 - w) + c[j+2] = "]=]\n" + j=j+3 + end + if z then + i = s + else + c[j] = "___[#___+1]=template.escape(" + c[j+1] = trim(sub(view, p, e - 1)) + c[j+2] = ")\n" + j=j+3 + s, i = e + 1, e + 2 + end + end + elseif t == "*" then + local e = find(view, "*}", p, true) + if e then + local z, w = escaped(view, s) + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, s - 1 - w) + c[j+2] = "]=]\n" + j=j+3 + end + if z then + i = s + else + c[j] = "___[#___+1]=template.output(" + c[j+1] = trim(sub(view, p, e - 1)) + c[j+2] = ")\n" + j=j+3 + s, i = e + 1, e + 2 + end + end + elseif t == "%" then + local e = find(view, "%}", p, true) + if e then + local z, w = escaped(view, s) + if z then + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, s - 1 - w) + c[j+2] = "]=]\n" + j=j+3 + end + i = s + else + local n = e + 2 + if sub(view, n, n) == "\n" then + n = n + 1 + end + local r = rpos(view, s - 1) + if i <= r then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, r) + c[j+2] = "]=]\n" + j=j+3 + end + c[j] = trim(sub(view, p, e - 1)) + c[j+1] = "\n" + j=j+2 + s, i = n - 1, n + end + end + elseif t == "(" then + local e = find(view, ")}", p, true) + if e then + local z, w = escaped(view, s) + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, s - 1 - w) + c[j+2] = "]=]\n" + j=j+3 + end + if z then + i = s + else + local f = sub(view, p, e - 1) + local x = find(f, ",", 2, true) + if x then + c[j] = "___[#___+1]=include([=[" + c[j+1] = trim(sub(f, 1, x - 1)) + c[j+2] = "]=]," + c[j+3] = trim(sub(f, x + 1)) + c[j+4] = ")\n" + j=j+5 + else + c[j] = "___[#___+1]=include([=[" + c[j+1] = trim(f) + c[j+2] = "]=])\n" + j=j+3 + end + s, i = e + 1, e + 2 + end + end + elseif t == "[" then + local e = find(view, "]}", p, true) + if e then + local z, w = escaped(view, s) + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, s - 1 - w) + c[j+2] = "]=]\n" + j=j+3 + end + if z then + i = s + else + c[j] = "___[#___+1]=include(" + c[j+1] = trim(sub(view, p, e - 1)) + c[j+2] = ")\n" + j=j+3 + s, i = e + 1, e + 2 + end + end + elseif t == "-" then + local e = find(view, "-}", p, true) + if e then + local x, y = find(view, sub(view, s, e + 1), e + 2, true) + if x then + local z, w = escaped(view, s) + if z then + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, s - 1 - w) + c[j+2] = "]=]\n" + j=j+3 + end + i = s + else + y = y + 1 + x = x - 1 + if sub(view, y, y) == "\n" then + y = y + 1 + end + local b = trim(sub(view, p, e - 1)) + if b == "verbatim" or b == "raw" then + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, s - 1 - w) + c[j+2] = "]=]\n" + j=j+3 + end + c[j] = "___[#___+1]=[=[" + c[j+1] = sub(view, e + 2, x) + c[j+2] = "]=]\n" + j=j+3 + else + if sub(view, x, x) == "\n" then + x = x - 1 + end + local r = rpos(view, s - 1) + if i <= r then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, r) + c[j+2] = "]=]\n" + j=j+3 + end + c[j] = 'blocks["' + c[j+1] = b + c[j+2] = '"]=include[=[' + c[j+3] = sub(view, e + 2, x) + c[j+4] = "]=]\n" + j=j+5 + end + s, i = y - 1, y + end + end + end + elseif t == "#" then + local e = find(view, "#}", p, true) + if e then + local z, w = escaped(view, s) + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, s - 1 - w) + c[j+2] = "]=]\n" + j=j+3 + end + if z then + i = s + else + e = e + 2 + if sub(view, e, e) == "\n" then + e = e + 1 + end + s, i = e - 1, e + end + end + end + s = find(view, "{", s + 1, true) + end + s = sub(view, i) + if s and s ~= "" then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = s + c[j+2] = "]=]\n" + j=j+3 + end + c[j] = "return layout and include(layout,setmetatable({view=table.concat(___),blocks=blocks},{__index=context})) or table.concat(___)" + return concat(c) +end + +function template.render(view, context, key, plain) + assert(view, "view was not provided for template.render(view, context, key, plain).") + return template.print(template.compile(view, key, plain)(context)) +end + +return template diff --git a/website/backend/lor/resty/template/html.lua b/website/backend/lor/resty/template/html.lua new file mode 100755 index 0000000..b3cbdfa --- /dev/null +++ b/website/backend/lor/resty/template/html.lua @@ -0,0 +1,51 @@ +local template = require "resty.template" +local setmetatable = setmetatable +local escape = template.escape +local concat = table.concat +local pairs = pairs +local type = type + +local function tag(name, content, attr) + local r, a, content = {}, {}, content or attr + r[#r + 1] = "<" + r[#r + 1] = name + if attr then + for k, v in pairs(attr) do + if type(k) == "number" then + a[#a + 1] = escape(v) + else + a[#a + 1] = k .. '="' .. escape(v) .. '"' + end + end + if #a > 0 then + r[#r + 1] = " " + r[#r + 1] = concat(a, " ") + end + end + if type(content) == "string" then + r[#r + 1] = ">" + r[#r + 1] = escape(content) + r[#r + 1] = "" + else + r[#r + 1] = " />" + end + return concat(r) +end + +local html = { __index = function(_, name) + return function(attr) + if type(attr) == "table" then + return function(content) + return tag(name, content, attr) + end + else + return tag(name, attr) + end + end +end } + +template.html = setmetatable(html, html) + +return template.html diff --git a/website/backend/lor/resty/template/microbenchmark.lua b/website/backend/lor/resty/template/microbenchmark.lua new file mode 100755 index 0000000..068c23c --- /dev/null +++ b/website/backend/lor/resty/template/microbenchmark.lua @@ -0,0 +1,148 @@ +local template = require "resty.template" + +local ok, new_tab = pcall(require, "table.new") +if not ok then + new_tab = function() return {} end +end + +local function run(iterations) + local gc, total, print, parse, compile, iterations, clock, format = collectgarbage, 0, ngx and ngx.say or print, template.parse, template.compile, iterations or 1000, os.clock, string.format + local view = [[ +
    + {% for _, v in ipairs(context) do %} +
  • {{v}}
  • + {% end %} +
]] + + print(format("Running %d iterations in each test", iterations)) + + gc() + gc() + + local x = clock() + for _ = 1, iterations do + parse(view, true) + end + local z = clock() - x + print(format(" Parsing Time: %.6f", z)) + total = total + z + + gc() + gc() + + x = clock() + for _ = 1, iterations do + compile(view, nil, true) + template.cache = {} + end + z = clock() - x + print(format("Compilation Time: %.6f (template)", z)) + total = total + z + + compile(view, nil, true) + + gc() + gc() + + x = clock() + for _ = 1, iterations do + compile(view, 1, true) + end + z = clock() - x + print(format("Compilation Time: %.6f (template, cached)", z)) + total = total + z + + local context = { "Emma", "James", "Nicholas", "Mary" } + + template.cache = {} + + gc() + gc() + + x = clock() + for _ = 1, iterations do + compile(view, 1, true)(context) + template.cache = {} + end + z = clock() - x + print(format(" Execution Time: %.6f (same template)", z)) + total = total + z + + template.cache = {} + compile(view, 1, true) + + gc() + gc() + + x = clock() + for _ = 1, iterations do + compile(view, 1, true)(context) + end + z = clock() - x + print(format(" Execution Time: %.6f (same template, cached)", z)) + total = total + z + + template.cache = {} + + local views = new_tab(iterations, 0) + for i = 1, iterations do + views[i] = "

Iteration " .. i .. "

\n" .. view + end + + gc() + gc() + + x = clock() + for i = 1, iterations do + compile(views[i], i, true)(context) + end + z = clock() - x + print(format(" Execution Time: %.6f (different template)", z)) + total = total + z + + gc() + gc() + + x = clock() + for i = 1, iterations do + compile(views[i], i, true)(context) + end + z = clock() - x + print(format(" Execution Time: %.6f (different template, cached)", z)) + total = total + z + + local contexts = new_tab(iterations, 0) + + for i = 1, iterations do + contexts[i] = { "Emma", "James", "Nicholas", "Mary" } + end + + template.cache = {} + + gc() + gc() + + x = clock() + for i = 1, iterations do + compile(views[i], i, true)(contexts[i]) + end + z = clock() - x + print(format(" Execution Time: %.6f (different template, different context)", z)) + total = total + z + + gc() + gc() + + x = clock() + for i = 1, iterations do + compile(views[i], i, true)(contexts[i]) + end + z = clock() - x + print(format(" Execution Time: %.6f (different template, different context, cached)", z)) + total = total + z + print(format(" Total Time: %.6f", total)) +end + +return { + run = run +} \ No newline at end of file diff --git a/website/backend/lor/spec/cases/basic_spec.lua b/website/backend/lor/spec/cases/basic_spec.lua new file mode 100644 index 0000000..8d3e4e9 --- /dev/null +++ b/website/backend/lor/spec/cases/basic_spec.lua @@ -0,0 +1,28 @@ +expose("expose modules", function() + package.path = '../../lib/?.lua;' .. '../../?.lua;'.. './lib/?.lua;' .. package.path + _G.lor = require("lor.index") + _G.request = require("spec.cases.mock_request") + _G.response = require("spec.cases.mock_response") + + _G.json_view = function(t) + local cjson + pcall(function() cjson = require("cjson") end) + if not cjson then + print("\n[cjson should be installed...]\n") + else + if t.root then + t:remove_nested_property(t.root) + print("\n", cjson.encode(t.root), "\n") + else + t:remove_nested_property(t) + print("\n", cjson.encode(t), "\n") + end + end + end + + _G._debug = nil + pcall(function() _G._debug = require("lor.lib.debug") end) + if not _G._debug then + _G._debug = print + end +end) diff --git a/website/backend/lor/spec/cases/common_spec.lua b/website/backend/lor/spec/cases/common_spec.lua new file mode 100755 index 0000000..e743c9f --- /dev/null +++ b/website/backend/lor/spec/cases/common_spec.lua @@ -0,0 +1,64 @@ +before_each(function() + lor = _G.lor + app = lor({ + debug = true + }) + Request = _G.request + Response = _G.response + req = Request:new() + res = Response:new() +end) + +after_each(function() + lor = nil + app = nil + Request = nil + Response = nil + req = nil + res = nil +end) + +describe("basic test for common usages", function() + it("use middleware should works.", function() + local count = 1 + app:use("/user", function(req, res, next) + count = 2 + next() + count = 5 + end) + + app:use("/user/123", function(req, res, next) + count = 3 + next() + end) + + app:get("/user/:id/create", function(req, res, next) + count = 4 + end) + + req.path ="/user/123/create" + req.method = "get" + app:handle(req, res) + assert.is.equals(count, 5) + end) + + it("error middleware should work.", function() + local origin_error_msg, error_msg = "this is an error", "" + app:use("/user", function(req, res, next) + next() + end) + + app:get("/user/123/create", function(req, res, next) + next(origin_error_msg) -- let other handlers continue... + end) + + app:erroruse(function(err, req, res, next) + error_msg = err + end) + + req.path = "/user/123/create" + req.method = "get" + app:handle(req, res) + assert.is.equals(error_msg, origin_error_msg) + end) +end) diff --git a/website/backend/lor/spec/cases/error_middleware_spec.lua b/website/backend/lor/spec/cases/error_middleware_spec.lua new file mode 100755 index 0000000..70852f0 --- /dev/null +++ b/website/backend/lor/spec/cases/error_middleware_spec.lua @@ -0,0 +1,108 @@ +before_each(function() + lor = _G.lor + app = lor({ + debug = true + }) + Request = _G.request + Response = _G.response + req = Request:new() + res = Response:new() +end) + +after_each(function() + lor = nil + app = nil + Request = nil + Response = nil + req = nil + res = nil +end) + + +describe("error middleware test:", function() + it("error middleware should stop the left error middlewares if has no `next`.", function() + local count = 1 + app:use("/user", function(req, res, next) + count = 2 + next() + end) + + app:use("/user/123", function(req, res, next) + count = 3 + next() + end) + + app:get("/user/123/create", function(req, res, next) + count = 4 + error("an error occurs") + end) + + app:erroruse(function(err, req, res, next) + count = 5 + end) + + app:erroruse(function(err, req, res, next) + count = 100 + end) + + req.path = "/user/123/create" + req.method = "get" + app:handle(req, res) + assert.is.equals(count, 5) + end) + + it("error middleware should continue the left error middlewares if has `next`.", function() + local count = 1 + app:use("/user", function(req, res, next) + count = 2 + next() + end) + + app:use("/user/123", function(req, res, next) + count = 3 + next() + end) + + app:get("/user/123/create", function(req, res, next) + count = 4 + error("an error occurs") + end) + + app:erroruse(function(err, req, res, next) + count = 5 + next(err) + end) + + app:erroruse(function(err, req, res, next) + assert.is.truthy(err) + count = 100 + end) + + req.path = "/user/123/create" + req.method = "get" + app:handle(req, res) + assert.is.equals(count, 100) + end) + + describe("if finall handler defined, it will always be executed", function() + it("error middleware should continue the left error middlewares if has `next`.", function() + local count = 1 + app:use("/user", function(req, res, next) + count = 2 + next() + end) + + app:use("/user/123", function(req, res, next) + count = 3 + next() + end) + + req.path = "/user/123/create" + req.method = "get" + app:handle(req, res, function(err) + count = 111 + end) + assert.is.equals(count, 111) + end) + end) +end) diff --git a/website/backend/lor/spec/cases/final_handler_spec.lua b/website/backend/lor/spec/cases/final_handler_spec.lua new file mode 100755 index 0000000..52c012c --- /dev/null +++ b/website/backend/lor/spec/cases/final_handler_spec.lua @@ -0,0 +1,178 @@ +before_each(function() + lor = _G.lor + app = lor({ + debug = true + }) + Request = _G.request + Response = _G.response + req = Request:new() + res = Response:new() +end) + +after_each(function() + lor = nil + app = nil + Request = nil + Response = nil + req = nil + res = nil +end) + + +-- remind: final handler is the last middleware that could be used to handle errors +-- it will alwayes be executed but `err` object is not nil only when error occurs +describe("if finall handler defined, it will always be executed.", function() + it("the request has no execution", function() + local count = 1 + app:use("/user", function(req, res, next) + count = 2 + next() + end) + + app:use("/user/123", function(req, res, next) + count = 3 + next() + end) + + req.path = "/user/123/create" + req.method = "get" + app:handle(req, res, function(err) + count = 111 + end) + assert.is.equals(count, 111) + end) + + it("404! should reach the final handler", function() + local count = 1 + + app:use("/user", function(req, res, next) -- won't enter + count = 2 + next() + end) + + app:get("/user/123", function(req, res, next) -- won't enter + count = 4 + next() + end) + + req.path = "/user/123/create" -- won't match app:get("/user/123", function...) + req.method = "get" + app:handle(req, res, function(err) -- 404! not found error + assert.is_truthy(err) + if err then + count = 404 + end + end) + assert.is.equals(404, count) + end) +end) + + +describe("the request has one successful execution. final handler execs but `err` should be nil.", function() + it("test case 2", function() + local count = 1 + app:use("/user", function(req, res, next) + count = 2 + next() + end) + + app:get("/user/123/create", function(req, res, next) + count = 4 + next() + end) + + req.path = "/user/123/create" + req.method = "get" + app:handle(req, res, function(err) + assert.is_falsy(err) -- err should be nil + assert.is_true(req:is_found()) -- matched app:get("/user/123/create") + count = 222 -- + end) + assert.is.equals(count, 222) + end) +end) + +describe("the previous error middleware pass or not pass the `err` object.", function() + it("test case 1.", function() + local count = 1 + app:use("/user", function(req, res, next) + count = 2 + next() + end) + + app:erroruse(function(err, req, res, next) + count = 5 + end) + + req.path = "/user/123/create" + req.method = "get" + app:handle(req, res, function(err) + assert.is.equals(count, 5) -- not found: should match error middleware, so count is 5 + count = 444 + if err then + count = 333 + end + + assert.is.equals(count, 333) + end) + end) + + + it("test case 2.", function() + local count = 1 + + app:get("/user/123", function(req, res, next) + count = 4 + error("abc") + end) + + app:erroruse(function(err, req, res, next) + count = 5 + assert.is.equals(true, string.find(err, "abc")>0) + next("def") + end) + + app:erroruse(function(err, req, res, next) + count = 6 + assert.is.equals("def", err) + next("123") + end) + + req.path = "/user/123" + req.method = "get" + app:handle(req, res, function(err) + count = 333 + if err then + count = 222 + end + assert.is.equals(222, count) + end) + end) + + it("test case 3, when error occurs in `error middleware`, the process will jump to the final handler immediately.", function() + app:get("/user/123", function(req, res, next) + error("ERROR1") + end) + + -- error middleware 1 + app:erroruse(function(err, req, res, next) + local test_var = 1 / tonumber("error number") -- error occurs here + next(err .. "\nERROR2") -- won't be reached + end) + + -- error middleware 2 + -- won't be matched because an error `in error middleware` occurs before it + app:erroruse(function(err, req, res, next) + next(err .. "\nERROR3") + end) + + req.path = "/user/123" + req.method = "get" + app:handle(req, res, function(err) + assert.is.equals(true, string.find(err, "ERROR1") > 0) + assert.is.equals(nil, string.find(err, "ERROR2")) -- matched `error middleware1`, but error occured + assert.is.equals(nil, string.find(err, "ERROR3")) -- not matched `error middleware2` + assert.is.equals(true, string.find(err, "perform arithmetic on a nil value") > 0) + end) + end) +end) diff --git a/website/backend/lor/spec/cases/group_index_route_spec.lua b/website/backend/lor/spec/cases/group_index_route_spec.lua new file mode 100644 index 0000000..dc657ef --- /dev/null +++ b/website/backend/lor/spec/cases/group_index_route_spec.lua @@ -0,0 +1,165 @@ +before_each(function() + lor = _G.lor + app = lor({ + debug = true + }) + Request = _G.request + Response = _G.response + req = Request:new() + res = Response:new() +end) + +after_each(function() + lor = nil + app = nil + Request = nil + Response = nil + req = nil + res = nil +end) + + +describe("group index route: basic usages", function() + it("should be error when giving wrong params", function() + local flag = 0 + local test_router = lor:Router() + + assert.has_error(function() test_router:get() end, "params should not be nil or empty") + assert.has_error(function() test_router:get({}) end, "params should not be nil or empty") + + assert.has_error(function() test_router:get("/test") end, "it must be an function if there's only one param") + assert.has_error(function() test_router:get("/test", "abc") end) + end) + + it("uri should mathed", function() + local flag = 0 + + local test_router = lor:Router() + test_router:get(function(req, res, next) + flag = 1 + end) + + app:use("/test", test_router()) + + req.path = "/test" + req.method = "get" + app:handle(req, res) + assert.is.equals(1, flag) + end) + + it("uri should not mathed", function() + local flag = 0 + + local test_router = lor:Router() + test_router:get(function(req, res, next) + flag = 1 + end) + + app:use("/test", test_router()) + app:erroruse(function(err, req, res, next) -- 404 error + assert.is.truthy(err) + assert.is.equals(false, req:is_found()) + flag = 999 + end) + + req.path = "/test/" + req.method = "get" + app:handle(req, res) + assert.is.equals(999, flag) + end) +end) + + +describe("group index route: multi funcs", function() + it("array params", function() + local flag = 0 + local test_router = lor:Router() + local func1 = function(req, res, next) + flag = 1 + next() + end + local func2 = function(req, res, next) + flag = 2 + next() + end + local last_func = function(req, res, next) + flag = 3 + end + test_router:post({func1, func2, last_func}) + app:use("/test", test_router()) + + req.path = "/test" + req.method = "post" + app:handle(req, res) + assert.is.equals(3, flag) + end) + + it("unpacked params", function() + local flag = 0 + local test_router = lor:Router() + local func1 = function(req, res, next) + flag = 1 + next() + end + local func2 = function(req, res, next) + flag = 2 + next() + end + local last_func = function(req, res, next) + flag = 3 + end + test_router:put(func1, func2, last_func) + app:use("/test", test_router()) + + req.path = "/test" + req.method = "put" + app:handle(req, res) + assert.is.equals(3, flag) + end) + + it("mixed params, case1", function() + local flag = 0 + local test_router = lor:Router() + local func1 = function(req, res, next) + flag = 1 + next() + end + local func2 = function(req, res, next) + flag = 2 + next() + end + local last_func = function(req, res, next) + flag = 3 + end + test_router:get({func1, func2}, last_func) + app:use("/test", test_router()) + + req.path = "/test" + req.method = "get" + app:handle(req, res) + assert.is.equals(3, flag) + end) + + it("mixed params, case2", function() + local flag = 0 + local test_router = lor:Router() + local func1 = function(req, res, next) + flag = 1 + next() + end + local func2 = function(req, res, next) + flag = 2 + next() + end + local last_func = function(req, res, next) + flag = 3 + end + test_router:put({func1}, func2, {last_func}) + app:use("/test", test_router()) + + req.path = "/test" + req.method = "put" + app:handle(req, res) + assert.is.equals(3, flag) + end) +end) diff --git a/website/backend/lor/spec/cases/group_router_spec.lua b/website/backend/lor/spec/cases/group_router_spec.lua new file mode 100755 index 0000000..49779db --- /dev/null +++ b/website/backend/lor/spec/cases/group_router_spec.lua @@ -0,0 +1,261 @@ +before_each(function() + lor = _G.lor + app = lor({ + debug = true + }) + Request = _G.request + Response = _G.response + req = Request:new() + res = Response:new() +end) + +after_each(function() + lor = nil + app = nil + Request = nil + Response = nil + req = nil + res = nil +end) + + +describe("single group router middleware", function() + it("basic usage", function() + local count = 0 + local userRouter = lor:Router() + + userRouter:get("/find/:id", function(req, res, next) + count = 1 + next() + end) + + userRouter:post("/create/:id", function(req, res, next) + count = 2 + next() + end) + + app:use("/user", userRouter()) + + -- should not be reached, because `next` has no params and no error occurs + app:erroruse(function(err, req, res, next) + count = 3 + end) + + req.path = "/user/create/123" + req.method = "post" + app:handle(req, res) + assert.is.equals(2, count) + + req.path = "/user/find/123" + req.method = "get" + app:handle(req, res) + assert.is.equals(1, count) + end) + + + it("error middleware should work as normal", function() + local count = 0 + local errorMsg = "an error occurs." + local userRouter = lor:Router() + + userRouter:get("/find/:id", function(req, res, next) + count = 1 + error(errorMsg) + end) + + userRouter:post("/create/:id", function(req, res, next) + count = 2 + next(errorMsg) -- has one param, so this will pass to an error middleware + end) + + userRouter:post("/edit/:id", function(req, res, next) + count = 3 + end) + + app:use("/user", userRouter()) + + app:erroruse(function(err, req, res, next) + if err then -- double check + count = err + req.params.id = "11111111" + end + end) + + req.path = "/user/find/456" + req.method = "get" + app:handle(req, res) + assert.is.truthy(string.match(count, errorMsg)) + + req.path = "/user/create/123" + req.method = "post" + app:handle(req, res) + assert.is.truthy(string.match(count, errorMsg)) -- count contains errorMsg + + req.path = "/user/edit/987" + req.method = "post" + app:handle(req, res) + assert.is.equals(3, count) -- no error occurs + end) + + it("path variable parser should work", function() + local count = 0 + local errorMsg = "an error occurs." + local userRouter = lor:Router() + + userRouter:get("/find/:id", function(req, res, next) + count = 1 + error(errorMsg) + end) + + userRouter:post("/create/:id", function(req, res, next) + count = 2 + next(errorMsg) -- has one param, so this will pass to an error middleware + end) + + userRouter:post("/edit/:id", function(req, res, next) + count = 3 + end) + + app:use("/user", userRouter()) + + app:erroruse("/user", function(err, req, res, next) + --print("user error middleware", err) + --if err then -- double check + count = err + req.params.id = "22222" + --end + next(err) -- 继续传递,只会被下一个erroruse覆盖 + end) + + app:erroruse(function(err, req, res, next) + --print("common error middleware", err) + if err then -- double check + count = err + req.params.id = "11111111" + end + end) + + req.path = "/user/find/456" + req.method = "get" + app:handle(req, res) + assert.is.equals("11111111", req.params.id) + assert.is.truthy(string.match(count, errorMsg)) + + req.path = "/user/create/123" + req.method = "post" + app:handle(req, res) + assert.is.equals("11111111", req.params.id) + assert.is.truthy(string.match(count, errorMsg)) -- count contains errorMsg + + req.path = "/user/edit/987" + req.method = "post" + app:handle(req, res) + assert.is.equals("987", req.params.id) + assert.is.equals(3, count) + end) +end) + +describe("multi group router middleware", function() + it("basic usage", function() + local flag = "" + local userRouter = lor:Router() + local bookRouter = lor:Router() + + app:use(function(req, res, next) + flag = "first use" + req.params.flag = "origin value" + next() + end) + + userRouter:get("/find/:id", function(req, res, next) + flag = 1 + req.params.flag = 1 + next("error occurs") + end) + + userRouter:post("/create/:id", function(req, res, next) + flag = 2 + req.params.flag = 2 + next() + end) + + bookRouter:get("/view/:id", function(req, res, next) + flag = 3 + req.params.flag = 3 + error("common error") + req.params.flag = 33 + end) + + app:use("/user", userRouter()) -- must invoke before `erroruse` + app:use("/book", bookRouter()) -- must invoke before `erroruse` + + app:erroruse("/user", function(err, req, res, next) + --print("------------- user error m", err) + assert.is.truthy(err) + flag = "user_error_middleware" + req.params.flag = 111 + end) + + app:erroruse(function(err, req, res, next) + --print("------------- common error m", err) + flag = "common_error_middleware" + req.params.flag = 333 + assert.is.truthy(err) + end) + + req.path = "/user/create/123" + req.method = "post" + app:handle(req, res) + assert.is.equals(2, flag) + assert.is.equals(2, req.params.flag) + + req.path = "/user/find/123" + req.method = "get" + app:handle(req, res) + assert.is.equals("user_error_middleware", flag) + assert.is.equals(111, req.params.flag) + + req.path = "/book/view/999" + req.method = "get" + app:handle(req, res, function(err) + if err then + print(err) + end + -- not found path will be here + -- not processed error will be here + end) + assert.is.equals("common_error_middleware", flag) + assert.is.equals(333, req.params.flag) + end) +end) + +describe("'use' the same group router middleware", function() + it("basic usage", function() + local userRouter = lor:Router() + + local reach_first = false + local reach_second = false + + userRouter:get("/get", function(req, res, next) + reach_first = true + reach_second = true + end) + + app:use("/user", userRouter()) + app:use("/u", userRouter()) + + req.path = "/user/get" + req.method = "get" + app:handle(req, res) + assert.is.equals(true, reach_first) + + -- reset values + reach_first = false + reach_second = false + + req.path = "/u/get" + req.method = "get" + app:handle(req, res) + assert.is.equals(true, reach_second) + end) +end) diff --git a/website/backend/lor/spec/cases/mock_request.lua b/website/backend/lor/spec/cases/mock_request.lua new file mode 100755 index 0000000..5086e2a --- /dev/null +++ b/website/backend/lor/spec/cases/mock_request.lua @@ -0,0 +1,35 @@ +local setmetatable = setmetatable + +local Request = {} + +function Request:new() + local body = {} -- body params + + local params = {} + + local instance = { + method = "", + query = {}, + params = {}, + body = body, + path = "", + url = "", + uri = "", + req_args = {}, + baseUrl = "", + found = false + } + setmetatable(instance, { __index = self }) + return instance +end + +function Request:is_found() + return self.found +end + +function Request:set_found(found) + self.found = found +end + + +return Request \ No newline at end of file diff --git a/website/backend/lor/spec/cases/mock_response.lua b/website/backend/lor/spec/cases/mock_response.lua new file mode 100755 index 0000000..424ddaa --- /dev/null +++ b/website/backend/lor/spec/cases/mock_response.lua @@ -0,0 +1,36 @@ +local setmetatable = setmetatable + +local Response = {} + +function Response:new() + local instance = { + headers = {}, + body = '--default body. you should not see this by default--', + view = nil, + } + + setmetatable(instance, {__index = self}) + return instance +end + +function Response:json(data) + self:_send(data) +end + +function Response:send(text) + self:_send(text) +end + +function Response:_send(content) + print(content) +end + +function Response:setHeader(key, value) +end + +function Response:status(code) + self.http_status = code + return self +end + +return Response diff --git a/website/backend/lor/spec/cases/multi_route_spec.lua b/website/backend/lor/spec/cases/multi_route_spec.lua new file mode 100644 index 0000000..b3b3b0f --- /dev/null +++ b/website/backend/lor/spec/cases/multi_route_spec.lua @@ -0,0 +1,254 @@ +before_each(function() + lor = _G.lor + app = lor({ + debug = true + }) + Request = _G.request + Response = _G.response + req = Request:new() + res = Response:new() +end) + +after_each(function() + lor = nil + app = nil + Request = nil + Response = nil + req = nil + res = nil +end) + + +describe("multi route: mounted on `app`", function() + it("array param", function() + local flag = 0 + local func1 = function(req, res, next) + flag = 1 + next() + end + local func2 = function(req, res, next) + flag = 2 + next() + end + local last_func = function(req, res, next) + flag = req.query.flag or 3 + end + app:get("/flag", {func1, func2, last_func}) + + app:erroruse(function(err, req, res, next) + assert.is.truthy(err) -- should not reach here. + flag = 999 + end) + + req.path = "/flag" + req.method = "get" + app:handle(req, res) + assert.is.equals(3, flag) + + req.path = "/flag" + req.query = {flag=111} + req.method = "get" + app:handle(req, res) + assert.is.equals(111, flag) + end) + + it("unpacked params", function() + local flag = 0 + local func1 = function(req, res, next) + flag = 1 + next() + end + local func2 = function(req, res, next) + flag = 2 + next() + end + local last_func = function(req, res, next) + flag = req.query.flag or 3 + end + app:get("/flag", func1, func2, last_func) + + app:erroruse(function(err, req, res, next) + assert.is.truthy(err) -- should not reach here. + flag = 999 + end) + + req.path = "/flag" + req.method = "get" + app:handle(req, res) + assert.is.equals(3, flag) + + req.path = "/flag" + req.query = {flag=111} + req.method = "get" + app:handle(req, res) + assert.is.equals(111, flag) + end) +end) + +describe("multi route: mounted on `group router`", function() + it("array param", function() + local flag = 0 + + local test_router = lor:Router() + local func1 = function(req, res, next) + flag = 1 + next() + end + local func2 = function(req, res, next) + flag = 2 + next() + end + local last_func = function(req, res, next) + flag = req.query.flag or 3 + end + test_router:get("/flag", {func1, func2, last_func}) + + app:use("/test", test_router()) + app:erroruse(function(err, req, res, next) + assert.is.truthy(err) -- should not reach here. + flag = 999 + end) + + req.path = "/test/flag" + req.method = "get" + app:handle(req, res) + assert.is.equals(3, flag) + + req.path = "/test/flag" + req.query = {flag=111} + req.method = "get" + app:handle(req, res) + assert.is.equals(111, flag) + end) + + it("unpacked params", function() + local flag = 0 + + local test_router = lor:Router() + local func1 = function(req, res, next) + flag = 1 + next() + end + local func2 = function(req, res, next) + flag = 2 + next() + end + local last_func = function(req, res, next) + flag = req.query.flag or 3 + end + test_router:get("/flag", func1, func2, last_func) + + app:use("/test", test_router()) + app:erroruse(function(err, req, res, next) + assert.is.truthy(err) -- should not reach here. + flag = 999 + end) + + req.path = "/test/flag" + req.method = "get" + app:handle(req, res) + assert.is.equals(3, flag) + + req.path = "/test/flag" + req.query = {flag=111} + req.method = "get" + app:handle(req, res) + assert.is.equals(111, flag) + end) +end) + +describe("multi route: muixed funcs for group router", function() + it("mixed params, case1", function() + local flag = 0 + local test_router = lor:Router() + local func1 = function(req, res, next) + flag = 1 + next() + end + local func2 = function(req, res, next) + flag = 2 + next() + end + local last_func = function(req, res, next) + flag = 3 + end + test_router:put("mixed", {func1, func2}, last_func) + app:use("/test", test_router()) + + req.path = "/test/mixed" + req.method = "put" + app:handle(req, res) + assert.is.equals(3, flag) + end) + + it("mixed params, case2", function() + local flag = 0 + local test_router = lor:Router() + local func1 = function(req, res, next) + flag = 1 + next() + end + local func2 = function(req, res, next) + flag = 2 + next() + end + local last_func = function(req, res, next) + flag = 3 + end + test_router:get("mixed", {func1}, func2, {last_func}) + app:use("/test", test_router()) + + req.path = "/test/mixed" + req.method = "get" + app:handle(req, res) + assert.is.equals(3, flag) + end) +end) + +describe("multi route: muixed funcs for `app`", function() + it("mixed params, case1", function() + local flag = 0 + local func1 = function(req, res, next) + flag = 1 + next() + end + local func2 = function(req, res, next) + flag = 2 + next() + end + local last_func = function(req, res, next) + flag = 3 + end + app:get("mixed", {func1, func2}, last_func) + + req.path = "/mixed" + req.method = "get" + app:handle(req, res) + assert.is.equals(3, flag) + end) + + it("mixed params, case2", function() + local flag = 0 + local func1 = function(req, res, next) + flag = 1 + next() + end + local func2 = function(req, res, next) + flag = 2 + next() + end + local func3 = function(req, res, next) + flag = 3 + next() + end + local last_func = function(req, res, next) + flag = 4 + end + app:get("mixed", {func1}, func2, {func3}, last_func) + + req.path = "/mixed" + req.method = "get" + app:handle(req, res) + assert.is.equals(4, flag) + end) +end) diff --git a/website/backend/lor/spec/cases/node_id_spec.lua b/website/backend/lor/spec/cases/node_id_spec.lua new file mode 100644 index 0000000..e86c9f2 --- /dev/null +++ b/website/backend/lor/spec/cases/node_id_spec.lua @@ -0,0 +1,38 @@ +before_each(function() + Trie = require("lor.lib.trie") + t = Trie:new() +end) + +after_each(function() + t = nil +end) + +function table_size(t) + local res = 0 + if t then + for _ in pairs(t) do + res = res + 1 + end + end + return res +end + +describe("node is should be unique", function() + it("test case 1", function() + local count = 100 + local nodes = {} + for i=1,count,1 do + local node = t:add_node(tostring(i)) + table.insert(nodes, node) + end + + assert.is.equals(count, #nodes) + assert.is.equals(count, table_size(nodes)) + + local node_map = {} + for i=1,count,1 do + node_map[nodes[i].id] = true + end + assert.is.equals(count, table_size(node_map)) + end) +end) diff --git a/website/backend/lor/spec/cases/not_found_spec.lua b/website/backend/lor/spec/cases/not_found_spec.lua new file mode 100755 index 0000000..769f13d --- /dev/null +++ b/website/backend/lor/spec/cases/not_found_spec.lua @@ -0,0 +1,125 @@ +before_each(function() + lor = _G.lor + app = lor({ + debug = true + }) + Request = _G.request + Response = _G.response + req = Request:new() + res = Response:new() +end) + +after_each(function() + lor = nil + app = nil + Request = nil + Response = nil + req = nil + res = nil +end) + +describe("not found test, error middleware and final handler should be reached correctly.", function() + it("test case 1", function() + local count = 0 + local errorMsg = "an error occurs." + + local userRouter = lor:Router() + userRouter:get("/find/:id", function(req, res, next) + count = 1 + error(errorMsg) + end) + app:use("/user", userRouter()) + + app:erroruse("/user", function(err, req, res, next) + count = err + req.params.id = "2222" + next(err) + end) + + app:erroruse(function(err, req, res, next) + count = err + req.params.id = "1111" + end) + + req.path = "/user/find/456" + req.method = "get" + app:handle(req, res) + assert.is.equals(req.params.id, "1111") + assert.is.equals(true, string.find(count, errorMsg) > 1) + end) + + it("test case 2", function() + local count = 0 + local errorMsg = "an error occurs." + + local userRouter = lor:Router() + userRouter:get("/find/:id", function(req, res, next) + count = 1 + error(errorMsg) + end) + app:use("/user", userRouter()) + + app:erroruse("/user", function(err, req, res, next) + count = err + req.params.id = "2222" + next(err) + end) + + app:erroruse(function(err, req, res, next) + count = "stop exec final handler" + req.params.id = "1111" + -- next(err) -- not invoke it, the final handler will not be reached! + end) + + req.params.id = nil --empty it + req.path = "/user/notfound" + req.method = "post" + app:handle(req, res, function(err) + if err then + count = "not found error catched" + end + end) + + assert.is.equals(404, res.http_status) + assert.is.equals(req.params.id, "1111") + assert.is.equals("stop exec final handler", count) + end) + + it("test case 3", function() + local count = 0 + local errorMsg = "an error occurs." + + local userRouter = lor:Router() + userRouter:get("/find/:id", function(req, res, next) + count = 1 + error(errorMsg) + end) + app:use("/user", userRouter()) + + app:erroruse("/user", function(err, req, res, next) + count = err + req.params.id = "2222" + next(err) + end) + + app:erroruse(function(err, req, res, next) + count = "stop exec final handler" + req.params.id = "1111" + next(err) -- invoke it, the final handler will be reached! + end) + + req.params.id = nil --empty it + req.path = "/notfound" + req.method = "post" + app:handle(req, res, function(err) + req.params.id = "3333" + if err then + count = "not found error catched" + end + end) + --print(app.router.trie:gen_graph()) + assert.is.equals(404, res.http_status) + assert.is.equals(req.params.id, "3333") + assert.is.equals("not found error catched", count) + end) +end) diff --git a/website/backend/lor/spec/cases/path_params_spec.lua b/website/backend/lor/spec/cases/path_params_spec.lua new file mode 100755 index 0000000..110d8c6 --- /dev/null +++ b/website/backend/lor/spec/cases/path_params_spec.lua @@ -0,0 +1,181 @@ +before_each(function() + lor = _G.lor + app = lor({ + debug = true + }) + Request = _G.request + Response = _G.response + req = Request:new() + res = Response:new() +end) + +after_each(function() + lor = nil + app = nil + Request = nil + Response = nil + req = nil + res = nil +end) + + +describe("test about variables parsed from path", function() + describe("path variables should be correct after parsed", function() + it("test case 1.", function() + app:use("/user", function(req, res, next) + req.params.default_var = "user" + next() + end) + + app:get("/user/:id/visit", function(req, res, next) + next() + end) + + req.path = "/user/3/visit" + req.method = "get" + app:handle(req, res) + + assert.is.equals('3', req.params.id) + assert.is.equals("user", req.params.default_var) + end) + + it("test case 2.", function() + app:use("/user", function(req, res, next) + assert.is.equals("3", req.params.id) + next() + end) + + app:get("/user/:id/visit", function(req, res, next) + req.params.id = 5 + next() + end) + + req.path = "/user/3/visit" + req.method = "get" + + app:handle(req, res) + assert.is.equals(5, req.params.id) + end) + + it("test case 3.", function() + app:get("/user/:id/visit", function(req, res, next) + error("error occurs") + req.params.id = '2' + end) + + app:erroruse("/user/:id/visit", function(err, req, res, next) + assert.is_not_nil(err) + req.params.id = 'error' + end) + + req.path = "/user/3/visit" + req.method = "get" + + app:handle(req, res) + assert.is.equals('error', req.params.id) + end) + + it("test case 4.", function() + app:use("/user", function(req, res, next) + req.params.id = '1' + next() + req.params.id = 'return' + end) + + app:get("/user/:id/visit", function(req, res, next) + error("error occurs") + req.params.id = '2' + end) + + app:erroruse("/user/:id/visit", function(err, req, res, next) + req.params.id = 'error' + end) + + req.path = "/user/3/visit" + req.method = "get" + + app:handle(req, res) + assert.is.equals('return', req.params.id) + end) + + it("test case 5.", function() + app:use("/user", function(req, res, next) + req.params.id = '1' + next() + req.params.id = 'return' + end) + + app:get("/user/:id/visit", function(req, res, next) + error("error occurs") + req.params.id = '2' + end) + + app:erroruse("/user/:id/visit", function(err, req, res, next) + req.params.id = 'error' + end) + + req.path = "/user/3/visit" + req.method = "get" + app:handle(req, res, function(err) + req.params.id = "from final handler" + end) + assert.is.equals('return', req.params.id) + end) + end) + + describe("path variables should be correctly parsed when the next request comes", function() + it("test case 1.", function() + app:use("/todo", function(req, res, next) + if req.params.id == "33" then + req.params.id = '3' + elseif req.params.id == "44" then + req.params.id = "4" + elseif req.params.id == "55" then + req.params.id = "5" + end + next() + end) + + app:post("/todo/delete/:id", function(req, res, next) + --print(req.params.id) + end) + + req.path = "/todo/delete/33" + req.method = "post" + app:handle(req, res) + assert.is.equals('3', req.params.id) + + req.path = "/todo/delete/44" + req.method = "post" + app:handle(req, res) + assert.is.equals('4', req.params.id) + + req.url = "/todo/delete/55" + req.path = req.url + req.method = "post" + app:handle(req, res) + assert.is.equals('5', req.params.id) + end) + + it("test case 2.", function() + app:use("/todo", function(req, res, next) + next() + end) + + app:post("/todo/view/:id/:name", function(req, res, next) + end) + + req.path = "/todo/view/44/two" + req.method = "post" + app:handle(req, res) + assert.is.equals('44', req.params.id) + assert.is.equals('two', req.params.name) + + req.path = "/todo/view/55/three" + req.method = "post" + app:handle(req, res) + assert.is.equals('55', req.params.id) + assert.is.equals('three', req.params.name) + end) + end) +end) diff --git a/website/backend/lor/spec/cases/path_pattern_1_spec.lua b/website/backend/lor/spec/cases/path_pattern_1_spec.lua new file mode 100755 index 0000000..c4b3ba4 --- /dev/null +++ b/website/backend/lor/spec/cases/path_pattern_1_spec.lua @@ -0,0 +1,113 @@ +before_each(function() + lor = _G.lor + app = lor({ + debug = false + }) + Request = _G.request + Response = _G.response + req = Request:new() + res = Response:new() + + count = 0 + + app:use("/", function(req, res, next) + count = 1 + next() + end) + + app:use("/user/", function(req, res, next) + count = 2 + next() + end) + app:use("/user/:id/view", function(req, res, next) + count = 3 + next() + end) + app:get("/user/123/view", function(req, res, next) + count = 4 + next() + end) + + app:post("/book" , function(req, res, next) + count = 5 + next() + end) + + local testRouter = lor:Router() -- 一个新的router,区别于主router + testRouter:get("/get", function(req, res, next) + count = 6 + next() + end) + testRouter:post("/foo/bar", function(req, res, next) + count = 7 + next() + end) + app:use("/test", testRouter()) + + app:erroruse(function(err, req, res, next) + count = 999 + end) +end) + +after_each(function() + lor = nil + app = nil + Request = nil + Response = nil + req = nil + res = nil +end) + +describe("next function usages test", function() + it("test case 1", function() + req.path = "/user/123/view" + req.method = "get" + app:handle(req, res, function(err) + assert.is_true(req:is_found()) + end) + + assert.is.equals(4, count) + assert.is.equals(nil, req.params.id) + end) + + it("test case 2", function() -- route found + app:conf("strict_route", false) -- 设置为非严格匹配 + req.path = "/user/123/view/" -- match app:get("/user/123/view", fn()) + req.method = "get" + app:handle(req, res, function(err) + assert.is_true(req:is_found()) + end) + + assert.is.equals(4, count) + assert.is.equals(nil, req.params.id) + end) + + it("test case 3", function() + req.path = "/book" + req.method = "get" + app:handle(req, res) + + assert.is.equals(999, count) + assert.is_nil( req.params.id) + + req.method = "post" -- post match + app:handle(req, res, function(err) + assert.is_true(req:is_found()) + end) + + assert.is.equals(5, count) + assert.is_nil( req.params.id) + end) + + it("test case 4", function() + req.path = "/notfound" + req.method = "get" + app:handle(req, res, function(err) + assert.is_not_true(req:is_found()) + assert.is_nil(err) + end) + + assert.is.equals(999, count) + assert.is_nil(req.params.id) + end) +end) diff --git a/website/backend/lor/spec/cases/path_pattern_2_spec.lua b/website/backend/lor/spec/cases/path_pattern_2_spec.lua new file mode 100755 index 0000000..e6514f7 --- /dev/null +++ b/website/backend/lor/spec/cases/path_pattern_2_spec.lua @@ -0,0 +1,70 @@ +before_each(function() + lor = _G.lor + app = lor({ + debug = false + }) + Request = _G.request + Response = _G.response + req = Request:new() + res = Response:new() + + count = 0 + match = 1 + + app:get("/all", function(req, res, next) + count = 1 + end) + + local testRouter = lor:Router() + testRouter:get("/all", function(req, res, next) + count = 6 + match = 2 + next() + end) + testRouter:get("/find/:type", function(req, res, next) + count = 7 + next() + end) + app:use("/test", testRouter()) + +end) + +after_each(function() + lor = nil + app = nil + Request = nil + Response = nil + req = nil + res = nil + match = nil +end) + +describe("path match test", function() + it("test case 1", function() + req.path = "/test/all" + req.method = "get" + app:handle(req, res) + assert.is.equals(2, match) + assert.is.equals(6, count) + end) + + it("test case 2", function() + req.path = "/test/find/all" + req.method = "get" + app:handle(req, res) + assert.is.equals(1, match) -- should not match "/test/all" + assert.is.equals(7, count) + end) + + it("test case 3", function() + req.path = "/test/find/all/1" + req.method = "get" + app:erroruse(function(err, req, res, next) -- 404 error + assert.is.truthy(err) + assert.is.equals(false, req:is_found()) + end) + app:handle(req, res) + assert.is.equals(1, match) + assert.is.equals(0, count) + end) +end) diff --git a/website/backend/lor/spec/cases/path_pattern_3_spec.lua b/website/backend/lor/spec/cases/path_pattern_3_spec.lua new file mode 100755 index 0000000..dc96f2d --- /dev/null +++ b/website/backend/lor/spec/cases/path_pattern_3_spec.lua @@ -0,0 +1,53 @@ +before_each(function() + lor = _G.lor + app = lor({ + debug = false + }) + Request = _G.request + Response = _G.response + req = Request:new() + res = Response:new() + + count = 0 + match = 1 + + app:get("/hello", function(req, res, next) + count = 1 + match = 2 + end) + + local testRouter = lor:Router() + testRouter:get("/hello", function(req, res, next) + match = 3 + end) + + app:use("/test", testRouter()) +end) + +after_each(function() + lor = nil + app = nil + Request = nil + Response = nil + req = nil + res = nil + match = nil +end) + + +describe("path match test", function() + it("test case 1", function() + req.path = "/test/hello" + req.method = "get" + app:handle(req, res) + assert.is.equals(3, match) + assert.is.equals(0, count) + end) + + it("test case 2", function() + req.path = "/hello" + req.method = "get" + app:handle(req, res) + assert.is.equals(2, match) + end) +end) diff --git a/website/backend/lor/spec/cases/uri_char_spec.lua b/website/backend/lor/spec/cases/uri_char_spec.lua new file mode 100644 index 0000000..72d8606 --- /dev/null +++ b/website/backend/lor/spec/cases/uri_char_spec.lua @@ -0,0 +1,45 @@ +before_each(function() + lor = _G.lor + app = lor({ + debug = false + }) + Request = _G.request + Response = _G.response + req = Request:new() + res = Response:new() + + flag = 0 +end) + +after_each(function() + lor = nil + app = nil + Request = nil + Response = nil + req = nil + res = nil + match = nil +end) + +describe("uri should support `-`", function() + it("test case 1", function() + req.path = "/a-b-c" + req.method = "get" + app:get("/a-b-c", function(req, res, next) + flag = 1 + end) + app:handle(req, res) + assert.is.equals(1, flag) + end) + + it("test case 2", function() + req.path = "/a_-b-/cde/-f" + req.method = "get" + app:get("/a_-b-/cde/-f", function(req, res, next) + flag = 2 + end) + app:handle(req, res) + assert.is.equals(2, flag) + end) + +end) diff --git a/website/backend/lor/spec/trie/basic_spec.lua b/website/backend/lor/spec/trie/basic_spec.lua new file mode 100644 index 0000000..64387a4 --- /dev/null +++ b/website/backend/lor/spec/trie/basic_spec.lua @@ -0,0 +1,27 @@ +expose("expose modules", function() + package.path = '../../lib/?.lua;' .. '../../?.lua;'.. './lib/?.lua;' .. package.path + _G.Trie = require("lor.lib.trie") + _G.Node = require("lor.lib.node") + + _G.json_view = function(t) + local cjson + pcall(function() cjson = require("cjson") end) + if not cjson then + print("\n[cjson should be installed...]\n") + else + if t.root then + t:remove_nested_property(t.root) + print("\n", cjson.encode(t.root), "\n") + else + t:remove_nested_property(t) + print("\n", cjson.encode(t), "\n") + end + end + end + + _G._debug = nil + pcall(function() _G._debug = require("lor.lib.debug") end) + if not _G._debug then + _G._debug = print + end +end) diff --git a/website/backend/lor/spec/trie/complex_cases_spec.lua b/website/backend/lor/spec/trie/complex_cases_spec.lua new file mode 100644 index 0000000..21d0bdd --- /dev/null +++ b/website/backend/lor/spec/trie/complex_cases_spec.lua @@ -0,0 +1,96 @@ +before_each(function() + Trie = _G.Trie + t = Trie:new() + t1 = Trie:new() + t2 = Trie:new() +end) + +after_each(function() + Trie = nil + t = nil + t1 = nil + t2 = nil + _debug = nil +end) + +describe("complex use cases: ", function() + it("should match the correct colon node.", function() + local n1 = t:add_node("/a/b/c/e") + local colon_n1 = t:add_node("/a/b/:name/d") + + local m1 = t:match("/a/b/c/d") + --print(t:gen_graph()) + assert.are.same(colon_n1, m1.node) + end) + + it("same prefix while different suffix.", function() + local n1 = t:add_node("/a/b/c/e/f") + local colon_n1 = t:add_node("/a/b/:name/d/g") + local colon_n2 = t:add_node("/a/b/:name/e/f") + + local m1 = t:match("/a/b/c/d/g") + assert.are.same(colon_n1, m1.node) + + local m2 = t:match("/a/b/other/e/f") + assert.are.same(colon_n2, m2.node) + end) + + it("confused prefix node", function() + local n1 = t:add_node("/people/:id") + local n2 = t:add_node("/people/list/:id") + local n3 = t:add_node("/people/list") + + local m1 = t:match("/people/123") + assert.are.same(n1, m1.node) + + local m2 = t:match("/people/list") + assert.are.same(n3, m2.node) + + local m3 = t:match("/people/list/123") + assert.are.same(n2, m3.node) + + local m4 = t:match("/people/list/123/456") + assert.are.same(nil, m4.node) + end) + + it("should succeed to match colon & common node.", function() + local n1 = t:add_node("/user") + local n2 = t:add_node("/user/123") + local n3 = t:add_node("/user/:id/create") + + local m1 = t:match("/user/123/create") + assert.are.same(n3, m1.node) -- should not match n2 + assert.is.equals("123", m1.params["id"]) + end) + + it("a complicated example.", function() + local n1 = t:add_node("/a/:p1/:p2/:p3/g") + local n2 = t:add_node("/a/:p1/:p2/f/h") + local n3 = t:add_node("/a/:p1/:p2/f") + local n4 = t:add_node("/a/:p1/e") + local n5 = t:add_node("/a/:p1/c/o") + local n6 = t:add_node("/a/d/c") + local n7 = t:add_node("/a/m") + + local m1 = t:match("/a/d/c/o") + local m2 = t:match("/a/n/e/f") + local m3 = t:match("/a/n/e/f/g") + + assert.are.same(n5, m1.node) + assert.is.equals("d", m1.params["p1"]) + + assert.are.same(n3, m2.node) + assert.is.equals("n", m2.params["p1"]) + assert.is.equals("e", m2.params["p2"]) + + assert.are.same(n1, m3.node) + assert.is.equals("n", m3.params["p1"]) + assert.is.equals("e", m3.params["p2"]) + assert.is.equals("f", m3.params["p3"]) + + end) +end) + + + + diff --git a/website/backend/lor/spec/trie/debug_cases.lua b/website/backend/lor/spec/trie/debug_cases.lua new file mode 100644 index 0000000..c08fb64 --- /dev/null +++ b/website/backend/lor/spec/trie/debug_cases.lua @@ -0,0 +1,49 @@ +setup(function() + _G.LOR_FRAMEWORK_DEBUG = false +end) + +teardown(function() +end) + +before_each(function() + Trie = _G.Trie + t = Trie:new() + t1 = Trie:new() + t2 = Trie:new() +end) + +after_each(function() + Trie = nil + t = nil + t1 = nil + t2 = nil + _debug = nil +end) + + + +describe("for debug cases: ", function() + it("a complicated example.", function() + local n1 = t:add_node("/a/:p1/:p2/:p3/g") + local n2 = t:add_node("/a/:p1/:p2/f/h") + local n3 = t:add_node("/a/:p1/:p2/f") + local n4 = t:add_node("/a/:p1/e") + local n5 = t:add_node("/a/:p1/c/o") + local n6 = t:add_node("/a/d/c") + local n7 = t:add_node("/a/m") + + local m3 = t:match("/a/n/e/f/g") + --json_view(t) + --print(t:gen_graph()) + + assert.are.same(n1, m3.node) + assert.is.equals("n", m3.params["p1"]) + assert.is.equals("e", m3.params["p2"]) + assert.is.equals("f", m3.params["p3"]) + + end) +end) + + + + diff --git a/website/backend/lor/spec/trie/define_node_spec.lua b/website/backend/lor/spec/trie/define_node_spec.lua new file mode 100644 index 0000000..048166d --- /dev/null +++ b/website/backend/lor/spec/trie/define_node_spec.lua @@ -0,0 +1,315 @@ +setup(function() + _G.LOR_FRAMEWORK_DEBUG = true +end) + +teardown(function() +end) + +before_each(function() + Trie = _G.Trie + t = Trie:new() + t1 = Trie:new() + t2 = Trie:new() +end) + +after_each(function() + Trie = nil + t = nil + t1 = nil + t2 = nil + _debug = nil +end) + +describe("objects check: ", function() + it("objects or modules should not be nil.", function() + assert.is.truthy(Trie) + assert.is.truthy(Node) + assert.is.truthy(t) + assert.is.truthy(t1) + assert.is.truthy(t2) + end) +end) + +describe("add node: ", function() + it("wrong patterns with `/` to add.", function() + assert.has_error(function() + t1:add_node("//") + end) + assert.has_error(function() + t1:add_node("///") + end) + assert.has_error(function() + t1:add_node("/a/b//") + end) + assert.has_error(function() + t1:add_node("//a/b/") + end) + assert.has_error(function() + t1:add_node("/a//b/") + end) + assert.has_error(function() + t1:add_node("/a///b/") + end) + end) + + it("wrong patterns with spaces to add.", function() + assert.has_error(function() + t1:add_node(" / / ") + end) + + assert.has_error(function() + t1:add_node("/ /") + end) + end) + + it("correct pattern to add.", function() + assert.has_no_error(function() + t1:add_node(" ") -- slim to "" + t1:add_node("/") + t1:add_node("") + t1:add_node("/a") + t1:add_node("/a/b") + t1:add_node("/a/b/c/") + end) + + assert.has_no.error(function() + t1:add_node(" ") + end) + end) + + it("these patterns should get same node.", function() + local node = t1:add_node("/") + + assert.is.equals(node, t1:add_node("/")) + assert.is.equals(node, t1:add_node("")) + assert.is.equals(t1:add_node("/"), t1:add_node("")) + assert.is.equals(node.parent, t1.root) + + assert.is_not.equals(node, t2:add_node("/")) + assert.is_not.equals(node, t2:add_node("")) + end) + + it("spaces in patterns are trimed.", function() + local node = t:add_node("/") + assert.is.equals(node, t:add_node(" ")) + assert.is.equals(node, t:add_node(" ")) + assert.is.equals(node, t:add_node(" /")) + assert.is.equals(node, t:add_node(" / ")) + + end) +end) + +describe("illegal & legal path: ", function() + it("wrong path", function() + assert.has_error(function() + t:add_node("/#+") + end) + assert.has_error(function() + t:add_node(":+abc") + end) + assert.has_error(function() + t:add_node(":&abc") + end) + + assert.has_error(function() + t:add_node(":abc*") + end) + + end) + it("correct path", function() + assert.has_no_error(function() + t:add_node(":abc") + end) + assert.has_no_error(function() + t:add_node("abc") + end) + assert.has_no_error(function() + t:add_node("/abc") + end) + end) +end) + +describe("node's name: ", function() + it("check names", function() + local node = t1:add_node("/") + assert.is.equals(node.name, "") + end) +end) + +describe("parent/children relation: ", function() + it("use case 1.", function() + local node = t1:add_node("/a/b") + assert.is.equals(node.name, "") + assert.is.equals(node.pattern, "/a/b") + + assert.is.equals(node, t1:add_node("/a/b")) + assert.is_not.equals(node, t1:add_node("a/b/")) + assert.is_not.equals(node, t1:add_node("/a/b/")) + assert.is.equals(t1:add_node("/a/b/"), t1:add_node("a/b/")) + + parent = t1:add_node("/a") + assert.is.equals(node.parent, parent) + assert.is_not.equals(parent.varyChild, node) + assert.is.equals(parent:find_child("b"), node) + child = t1:add_node("/a/b/c") + assert.is.equals(child.parent, node) + assert.is.equals(node:find_child("c"), child) + + assert.has_error(function() + t1:add_node("/a//b") + end) + end) + + it("use case 2.", function() + local root = t.root + + local slash_level = t:add_node("/") + local level0 = t:add_node("/00") + local level1 = t:add_node("/01") + local level2 = t:add_node("/02") + + local level0_0 = t:add_node("/00/0") + local level0_1 = t:add_node("/00/1") + local level0_2 = t:add_node("/00/2") + + local level1_0 = t:add_node("/01/0") + local level1_1 = t:add_node("/01/1") + local level1_2 = t:add_node("/01/2") + + local level2_0 = t:add_node("/02/0") + local level2_1 = t:add_node("/02/1") + local level2_2 = t:add_node("/02/2") + + local level0_0_0 = t:add_node("/00/0/0") + local level0_0_1 = t:add_node("/00/0/1") + local level0_0_2 = t:add_node("/00/0/2") + + assert.is.equals(root.name, "") + assert.is.equals(root.pattern, "") + + assert.is.equals(slash_level.name, "") + assert.is.equals(slash_level.pattern, "/") + assert.is.equals(level0.name, "") + assert.is.equals(level0.pattern, "/00") + + assert.are.same(slash_level.parent, level1.parent) + assert.are.same(level0.parent, level1.parent) + assert.are.same(level1.parent, level2.parent) + + assert.is.equals(root, level0.parent) + assert.is.equals(root, level1.parent) + assert.is.equals(root, level2.parent) + + assert.is.equals(level0, level0_0.parent) + assert.is.equals(level0_0.parent, level0_1.parent) + assert.is.equals(level1, level1_0.parent) + assert.is.equals(level1_0.parent, level1_1.parent) + + assert.is.equals(root, level0_0_0.parent.parent.parent) + assert.is.equals(level0, level0_0_0.parent.parent) + end) +end) + +describe("colon child define: ", function() + it("should failed to define conflict node.", function() + local root = t.root + + local slash_level = t:add_node("/") + local level0 = t:add_node("/00") + + t:add_node("/00/0") + t:add_node("/00/0/:0") + + assert.has_error(function() + t:add_node("/00/0/:1") + end) + + assert.has_error(function() + t:add_node("/00/0/:01") + end) + + assert.has_no_error(function() + t:add_node("/00/0/absolute") + end) + end) + + it("should succeed to define not conflict nodes.", function() + local root = t.root + + local slash_level = t:add_node("/") + local level0 = t:add_node("/00") + + t:add_node("/00/0") + t:add_node("/00/0/:0") + + assert.has_no_error(function() + t:add_node("/00/0/absolute") + end) + + assert.has_no_error(function() + t:add_node("/00/0/123") + end) + + assert.has_no_error(function() + t:add_node("/00/0/123/456") + end) + end) +end) + +describe("shadow child define: ", function() + it("should failed to define conflict node.", function() + local level0_0_0 = t:add_node("/00/0/:0") + local level0_0_0_shadow = t:add_node("/00/0/:0") + local level0_0_0_shadow2 = t:add_node("/00/0/:0") + assert.is.equals(level0_0_0, level0_0_0_shadow) + assert.is.equals(level0_0_0_shadow, level0_0_0_shadow2) + end) +end) + +describe("regex node define: ", function() + it("should failed to define error regex node.", function() + assert.has_error(function() + t:add_node("/a/:(^abc)") + end) + assert.has_error(function() + t:add_node("/a/(^abc)") + end) + assert.has_error(function() + t:add_node("/a/:abc(^abc") + end) + assert.has_error(function() + t:add_node("/a/:abc^abc)") + end) + end) + it("should succeed to define regex node.", function() + assert.has_no_error(function() + t:add_node("/a/:b(^abc)") + end) + assert.has_no_error(function() + t:add_node("/a/b/:c(^abc)") + end) + + + local n1 = t:add_node("/a/:b(^abc)") + assert.is.equals("^abc", n1.regex) + end) +end) + + +describe("just for dev, print json/tree : ", function() + it("case 1.", function() + local root = t.root + + local slash_level = t:add_node("/") + local level0 = t:add_node("/00") + local level0_0 = t:add_node("/00/0") + + local level0_0_1 = t:add_node("/00/0/1") + + local level0_0_0 = t:add_node("/00/0/:0") + local level0_0_0_shadow = t:add_node("/00/0/:0") + assert.is.equals(level0_0_0, level0_0_0_shadow) + + end) +end) + diff --git a/website/backend/lor/spec/trie/find_node_spec.lua b/website/backend/lor/spec/trie/find_node_spec.lua new file mode 100644 index 0000000..84d698c --- /dev/null +++ b/website/backend/lor/spec/trie/find_node_spec.lua @@ -0,0 +1,162 @@ +setup(function() + _G.LOR_FRAMEWORK_DEBUG = false +end) + +teardown(function() +end) + +before_each(function() + Trie = _G.Trie + t = Trie:new() + t1 = Trie:new() + t2 = Trie:new() +end) + +after_each(function() + Trie = nil + t = nil + t1 = nil + t2 = nil + _debug = nil +end) + +describe("path match: ", function() + it("should succeed to match colon node.", function() + local n1 = t:add_node("/a/:name") + + local m1 = t:match("/a/b") + assert.are.same(n1, m1.node) + local m2 = t:match("/a/demo") + assert.are.same(n1, m2.node) + assert.is.equals("demo", m2.params.name) + + t.strict_route = false + local m3 = t:match("/a/mock/") + assert.are.same(n1, m3.node) + assert.is.equals("mock", m3.params["name"]) + + t.strict_route = true + m3 = t:match("/a/mock/") + assert.are.same(nil, m3.node) + end) +end) + +describe("path matched pipeline: ", function() + it("should get correct pipeline.", function() + local n0 = t:add_node("/a") + local n1 = t:add_node("/a/b") + local n2 = t:add_node("/a/b/c") + + local m1 = t:match("/a/b/c") + assert.are.same(n2, m1.node) + + local p1 = m1.pipeline + assert.is.equals(4, #m1.pipeline) + assert.is.equals(t.root.id, p1[1].id) + assert.is.equals(n0.id, p1[2].id) + assert.is.equals(n1.id, p1[3].id) + assert.is.equals(n2.id, p1[4].id) + end) + + it("slash node not included in pipeline.", function() + local slash_node = t:add_node("/") + local n0 = t:add_node("/a") + local n1 = t:add_node("/a/b") + local n2 = t:add_node("/a/b/c") + + local m1 = t:match("/a/b/c") -- won't match slash_node + assert.are.same(n2, m1.node) + + local p1 = m1.pipeline + assert.is_not.equals(5, #m1.pipeline) + assert.is.equals(4, #m1.pipeline) + for i, v in ipairs(p1) do + assert.is_not.equals(slash_node.id, v.id) + end + end) + + it("pipeline contains the right parent node.", function() + local n1 = t:add_node("/a/b/c") + + local m1 = t:match("/a/b/c") + assert.are.same(n1, m1.node) + + local p1 = m1.pipeline + assert.is.equals(4, #m1.pipeline) + assert.is.equals(t.root.id, p1[1].id) + assert.is.equals(n1.parent.parent.id, p1[2].id) + assert.is.equals(n1.parent.id, p1[3].id) + assert.is.equals(n1.id, p1[4].id) + end) + + it("children pipelines should contain same parents node.", function() + local n1 = t:add_node("/a/b/c") + local colon_n1 = t:add_node("/a/b/:name") + + local m1 = t:match("/a/b/c") + assert.are.same(n1, m1.node) + + local m2 = t:match("/a/b/sumory") + assert.are.same(colon_n1, m2.node) + + local p1 = m1.pipeline + assert.is.equals(4, #m1.pipeline) + assert.is.equals(t.root.id, p1[1].id) + assert.is.equals(n1.parent.parent.id, p1[2].id) + assert.is.equals(n1.parent.id, p1[3].id) + assert.is.equals(n1.id, p1[4].id) + + local p2 = m2.pipeline + assert.is.equals(4, #m2.pipeline) + assert.is.equals(t.root.id, p2[1].id) + assert.is.equals(colon_n1.parent.parent.id, p2[2].id) + assert.is.equals(colon_n1.parent.id, p2[3].id) + assert.is.equals(colon_n1.id, p2[4].id) + + assert.is.equals(p1[1].id, p2[1].id) + assert.is.equals(p1[2].id, p2[2].id) + assert.is.equals(p1[3].id, p2[3].id) + assert.is_not.equals(p1[4].id, p2[4].id) + end) +end) + + +describe("use cases that are hard to understand: ", function() + it("absolute & colon node math.", function() + local n1 = t:add_node("/a/b/c") + local colon_n1 = t:add_node("/a/b/:name") + + local m1 = t:match("/a/b/c") + assert.are.same(n1, m1.node) + + local m2 = t:match("/a/b/sumory") + assert.are.same(colon_n1, m2.node) + + end) + + it("confused prefix node", function() + local n1 = t:add_node("/people/:id") + local n2 = t:add_node("/people/list/:id") + + local m1 = t:match("/people/1") + assert.are.same(n1, m1.node) + local m11 = t:match("/people/abc") + assert.are.same(n1, m11.node) + + local m2 = t:match("/people/abc/123") + assert.are.same(nil, m2.node) + + local m3 = t:match("/people/list/123") + assert.are.same(n2, m3.node) + end) + + it("children pipelines should contain same parents node.", function() + local n1 = t:add_node("/a/b/c") + local colon_n1 = t:add_node("/a/b/:name") + + local m1 = t:match("/a/b/c") + assert.are.same(n1, m1.node) + end) +end) + + diff --git a/website/backend/lor/spec/trie/handle_spec.lua b/website/backend/lor/spec/trie/handle_spec.lua new file mode 100644 index 0000000..bba5c9d --- /dev/null +++ b/website/backend/lor/spec/trie/handle_spec.lua @@ -0,0 +1,68 @@ +setup(function() + _G.LOR_FRAMEWORK_DEBUG = false +end) + +teardown(function() +end) + +before_each(function() + Trie = _G.Trie + t = Trie:new() + t1 = Trie:new() + t2 = Trie:new() +end) + +after_each(function() + Trie = nil + t = nil + t1 = nil + t2 = nil + _debug = nil +end) + +describe("`handler` test cases: ", function() + it("should succeed to define handlers.", function() + local n1 = t:add_node("/a") + local m1 = t:add_node("/a/b") + local m2 = t:add_node("/a/c") + local m3 = t:add_node("/a/:name") + + n1:handle("get", function(req, res, next) end) + assert.is.equals(1, #n1.handlers["get"]) + + n1:handle("post", function(req, res, next) end) + assert.is.equals(1, #n1.handlers["post"]) + + n1:handle("put", function(req, res, next) end, function(req, res, next) end, function(req, res, next) end) + assert.is.equals(3, #n1.handlers["put"]) + + n1:handle("DELETE", {function(req, res, next) end, function(req, res, next) end}) + assert.is.equals(2, #n1.handlers["delete"]) + + m2:handle("get", function(req, res, next) end) + assert.is.equals(1, #m2.handlers["get"]) + end) + + it("should failed to define handlers.", function() + local n1 = t:add_node("/a") + + assert.has_error(function() + n1:handle("getabc", function(req, res, next) end) -- wrong `method` name + end) + + assert.has_error(function() + n1:handle("get", {}) + end) + + assert.has_error(function() + n1:handle("get", function(req, res, next) end) + n1:handle("get", function(req, res, next) end) -- define handler repeatly + end) + + -- _G.LOR_FRAMEWORK_DEBUG = true + --_debug(n1) + --json_view(t) + --print(t:gen_graph()) + end) +end) + diff --git a/website/backend/lor/spec/trie/strict_route_spec.lua b/website/backend/lor/spec/trie/strict_route_spec.lua new file mode 100644 index 0000000..e036746 --- /dev/null +++ b/website/backend/lor/spec/trie/strict_route_spec.lua @@ -0,0 +1,73 @@ +setup(function() + _G.LOR_FRAMEWORK_DEBUG = false +end) + +teardown(function() +end) + +before_each(function() + Trie = _G.Trie + t = Trie:new() + t1 = Trie:new() + t2 = Trie:new() +end) + +after_each(function() + Trie = nil + t = nil + t1 = nil + t2 = nil + _debug = nil +end) + +describe("strict route: ", function() + it("should match if strict route is false.", function() + local t = Trie:new({ + strict_route = false -- default value is true + }) + local n1 = t:add_node("/a/b") + + local m1 = t:match("/a/b/") + assert.are.same(n1, m1.node) + + local m2 = t:match("/a/b") + assert.are.same(n1, m2.node) + end) + + it("should not match if strict route is true.", function() + local t = Trie:new({ + strict_route = true -- default value is true + }) + local n1 = t:add_node("/a/b") + + local m1 = t:match("/a/b") + assert.are.same(n1, m1.node) + + local m2 = t:match("/a/b/") + assert.is.falsy(m2.node) + end) + + it("should match if strict route is false and the exact route is not given.", function() + local t = Trie:new({ + strict_route = false -- default value is true + }) + local n1 = t:add_node("/a/b") + + local m1 = t:match("/a/b/") + assert.are.same(n1, m1.node) + end) + + it("should not match if strict route is true and the exact route is not given.", function() + local t = Trie:new({ + strict_route = true -- default value is true + }) + local n1 = t:add_node("/a/b") + + local m1 = t:match("/a/b/") + assert.is.falsy(m1.node) + assert.is.equals(nil, m1.node) + end) +end) + + + diff --git a/website/backend/reload.sh b/website/backend/reload.sh new file mode 100755 index 0000000..b38966d --- /dev/null +++ b/website/backend/reload.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +##################################################################### +# usage: +# sh reload.sh -- reload application @dev +# sh reload.sh ${env} -- reload application @${env} + +# examples: +# sh reload.sh prod -- use conf/nginx-prod.conf to reload OpenResty +# sh reload.sh -- use conf/nginx-dev.conf to reload OpenResty +##################################################################### + +if [ -n "$1" ];then + PROFILE="$1" +else + PROFILE=dev +fi + +mkdir -p logs & mkdir -p tmp +echo "reload lor application with profile: "${PROFILE} +/usr/local/openresty/nginx/sbin/nginx -s reload -p `pwd`/ -c conf/nginx-${PROFILE}.conf diff --git a/website/backend/start.sh b/website/backend/start.sh new file mode 100755 index 0000000..5a21bfc --- /dev/null +++ b/website/backend/start.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +##################################################################### +# usage: +# sh start.sh -- start application @dev +# sh start.sh ${env} -- start application @${env} + +# examples: +# sh start.sh prod -- use conf/nginx-prod.conf to start OpenResty +# sh start.sh -- use conf/nginx-dev.conf to start OpenResty +##################################################################### + +if [ -n "$1" ];then + PROFILE="$1" +else + PROFILE=dev +fi + +mkdir -p logs & mkdir -p tmp +echo "start lor application with profile: "${PROFILE} +/usr/local/openresty/nginx/sbin/nginx -p `pwd`/ -c conf/nginx-${PROFILE}.conf diff --git a/website/backend/stop.sh b/website/backend/stop.sh new file mode 100755 index 0000000..26788c7 --- /dev/null +++ b/website/backend/stop.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +##################################################################### +# usage: +# sh stop.sh -- stop application @dev +# sh stop.sh ${env} -- stop application @${env} + +# examples: +# sh stop.sh prod -- use conf/nginx-prod.conf to stop OpenResty +# sh stop.sh -- use conf/nginx-dev.conf to stop OpenResty +##################################################################### + +if [ -n "$1" ];then + PROFILE="$1" +else + PROFILE=dev +fi + +mkdir -p logs & mkdir -p tmp +echo "stop lor application with profile: "${PROFILE} +/usr/local/openresty/nginx/sbin/nginx -s stop -p `pwd`/ -c conf/nginx-${PROFILE}.conf diff --git a/website/backend/www/.gitkeep b/website/backend/www/.gitkeep new file mode 100755 index 0000000..e69de29 diff --git a/website/frontend/.browserslistrc b/website/frontend/.browserslistrc new file mode 100644 index 0000000..d6471a3 --- /dev/null +++ b/website/frontend/.browserslistrc @@ -0,0 +1,2 @@ +> 1% +last 2 versions diff --git a/website/frontend/.editorconfig b/website/frontend/.editorconfig new file mode 100644 index 0000000..685c1bf --- /dev/null +++ b/website/frontend/.editorconfig @@ -0,0 +1,35 @@ +# http://editorconfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +# Indentation override for js(x), ts(x) and vue files +[*.{js,jsx,ts,tsx,vue}] +indent_size = 2 +indent_style = space + +# Indentation override for css related files +[*.{css,styl,scss,less,sass}] +indent_size = 2 +indent_style = space + +# Indentation override for html files +[*.html] +indent_size = 2 +indent_style = space + +# Trailing space override for markdown file +[*.md] +trim_trailing_whitespace = false + +# Indentation override for config files +[*.{json,yml}] +indent_size = 2 +indent_style = space diff --git a/website/frontend/.env b/website/frontend/.env new file mode 100644 index 0000000..e74863b --- /dev/null +++ b/website/frontend/.env @@ -0,0 +1,2 @@ +NODE_ENV = 'production' +VUE_APP_BASE_API = 'http://127.0.0.1:9527/' \ No newline at end of file diff --git a/website/frontend/.eslintignore b/website/frontend/.eslintignore new file mode 100644 index 0000000..7b4fc4f --- /dev/null +++ b/website/frontend/.eslintignore @@ -0,0 +1,3 @@ +dist/*.js +src/assets +tests/unit/coverage diff --git a/website/frontend/.eslintrc.js b/website/frontend/.eslintrc.js new file mode 100644 index 0000000..0e305dc --- /dev/null +++ b/website/frontend/.eslintrc.js @@ -0,0 +1,37 @@ +module.exports = { + root: true, + env: { + browser: true, + node: true, + es6: true + }, + parserOptions: { + parser: '@typescript-eslint/parser', + sourceType: 'module' + }, + plugins: [ + 'vue' + ], + rules: { + 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', + 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', + 'space-before-function-paren': [2, 'never'], + 'vue/array-bracket-spacing': 'error', + 'vue/arrow-spacing': 'error', + 'vue/block-spacing': 'error', + 'vue/brace-style': 'error', + 'vue/camelcase': 'error', + 'vue/comma-dangle': 'error', + 'vue/component-name-in-template-casing': 'error', + 'vue/eqeqeq': 'error', + 'vue/key-spacing': 'error', + 'vue/match-component-file-name': 'error', + 'vue/object-curly-spacing': 'error' + }, + 'extends': [ + 'eslint:recommended', + 'plugin:vue/recommended', + '@vue/standard', + '@vue/typescript' + ] +} diff --git a/website/frontend/.gitignore b/website/frontend/.gitignore new file mode 100644 index 0000000..b11194a --- /dev/null +++ b/website/frontend/.gitignore @@ -0,0 +1,25 @@ +.DS_Store +node_modules +/dist + +/tests/e2e/videos/ +/tests/e2e/screenshots/ +/tests/**/coverage/ + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw* diff --git a/website/frontend/LICENSE b/website/frontend/LICENSE new file mode 100644 index 0000000..7c3565d --- /dev/null +++ b/website/frontend/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Chong Guo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/website/frontend/README-zh.md b/website/frontend/README-zh.md new file mode 100644 index 0000000..c55a024 --- /dev/null +++ b/website/frontend/README-zh.md @@ -0,0 +1,94 @@ +# vue-typescript-admin-template + +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com) +[![CircleCI](https://circleci.com/gh/Armour/vue-typescript-admin-template/tree/minimal.svg?style=shield)](https://circleci.com/gh/Armour/vue-typescript-admin-template/tree/minimal) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) +[![Template from jarvis](https://img.shields.io/badge/Hi-Jarvis-ff69b4.svg)](https://github.com/Armour/Jarvis) + +[English](./README.md) | 简体中文 + +## 总览 + +这是一个极简的 vue typescript admin 管理后台。它只包含了 Element UI & axios & svgicon & permission control & lint,这些搭建后台必要的东西。部分源代码是由 [vue-cli](https://github.com/vuejs/vue-cli) 和 [jarvis](https://github.com/Armour/Jarvis) 自动生成的。Mock 部分直接使用了我预先搭建的 [Mock 服务器](https://github.com/armour/vue-typescript-admin-mock-server)。 + +## 截图/动图 + +![demo](./demo/demo.gif) + +## 相关项目 + +[Armour/vue-typescript-admin-mock-server](https://github.com/armour/vue-typescript-admin-mock-server) (Mock 服务器) + +[Armour/vue-typescript-admin-docs](https://github.com/armour/vue-typescript-admin-docs) (项目文档) + +Javascript 版本相关: + +[PanJiaChen/vue-admin-template](https://github.com/PanJiaChen/vue-admin-template) (a vue2.0 minimal admin template) + +[PanJiaChen/vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) (full features supported vue admin) + +[PanJiaChen/electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin) (a vue electron admin project) + +## 如何设置以及启动项目 + +### 安装依赖 + +```bash +yarn install +``` + +### 启动本地开发环境(自带热启动) + +```bash +yarn serve +``` + +### 构建生产环境 (自带压缩) + +```bash +yarn build +``` + +### 代码格式检查以及自动修复 + +```bash +yarn lint +``` + +### 运行单元测试 + +```bash +yarn test:unit +``` + +### 运行端对端测试 + +```bash +yarn test:e2e +``` + +### 自动生成 svg 组件 + +```bash +yarn svg +``` + +### 自定义 Vue 配置 + +看这里 [Configuration Reference](https://cli.vuejs.org/config/). + +## 浏览器支持 + +Modern browsers and Internet Explorer 10+. + +| [IE / Edge](http://godban.github.io/browsers-support-badges/)
IE / Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| IE10, IE11, Edge | last 2 versions | last 2 versions | last 2 versions | + +## 参与贡献 + +请看 [CONTRIBUTING.md](https://github.com/Armour/vue-typescript-admin-template/blob/master/.github/CONTRIBUTING.md) + +## License + +[MIT License](https://github.com/Armour/vue-typescript-admin-template/blob/master/LICENSE) diff --git a/website/frontend/README.md b/website/frontend/README.md new file mode 100644 index 0000000..f8afc9a --- /dev/null +++ b/website/frontend/README.md @@ -0,0 +1,92 @@ +# vue-typescript-admin-template + +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com) +[![CircleCI](https://circleci.com/gh/Armour/vue-typescript-admin-template/tree/minimal.svg?style=shield)](https://circleci.com/gh/Armour/vue-typescript-admin-template/tree/minimal) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) +[![Template from jarvis](https://img.shields.io/badge/Hi-Jarvis-ff69b4.svg)](https://github.com/Armour/Jarvis) + +English | [简体中文](./README-zh.md) + +## Overview + +A minimal vue typescript admin template with element-ui & axios & svgicon & permission control & lint, part of the code was generated by [vue-cli](https://github.com/vuejs/vue-cli) and [jarvis](https://github.com/Armour/Jarvis). + +## Screenshots + +![demo](./demo/demo.gif) + +## Related Project + +[Armour/vue-typescript-admin-mock-server](https://github.com/armour/vue-typescript-admin-mock-server) (mock server for this project) + +[Armour/vue-typescript-admin-docs](https://github.com/armour/vue-typescript-admin-docs) (documentation source for this project) + +Javascript version: + +[PanJiaChen/vue-admin-template](https://github.com/PanJiaChen/vue-admin-template) (a vue2.0 minimal admin template) + +[PanJiaChen/vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) (full features supported vue admin) + +[PanJiaChen/electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin) (a vue electron admin project) + +## Project setup + +```bash +yarn install +``` + +### Compiles and hot-reloads for development + +```bash +yarn serve +``` + +### Compiles and minifies for production + +```bash +yarn build +``` + +### Lints and fixes files + +```bash +yarn lint +``` + +### Run your unit tests + +```bash +yarn test:unit +``` + +### Run your end-to-end tests + +```bash +yarn test:e2e +``` + +### Generate all svg components + +```bash +yarn svg +``` + +### Customize Vue configuration + +See [Configuration Reference](https://cli.vuejs.org/config/). + +## Browsers support + +Modern browsers and Internet Explorer 10+. + +| [IE / Edge](http://godban.github.io/browsers-support-badges/)
IE / Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| IE10, IE11, Edge | last 2 versions | last 2 versions | last 2 versions | + +## Contributing + +See [CONTRIBUTING.md](https://github.com/Armour/vue-typescript-admin-template/blob/master/.github/CONTRIBUTING.md) + +## License + +[MIT License](https://github.com/Armour/vue-typescript-admin-template/blob/master/LICENSE) diff --git a/website/frontend/babel.config.js b/website/frontend/babel.config.js new file mode 100644 index 0000000..ba17966 --- /dev/null +++ b/website/frontend/babel.config.js @@ -0,0 +1,5 @@ +module.exports = { + presets: [ + '@vue/app' + ] +} diff --git a/website/frontend/cypress.json b/website/frontend/cypress.json new file mode 100644 index 0000000..470c720 --- /dev/null +++ b/website/frontend/cypress.json @@ -0,0 +1,3 @@ +{ + "pluginsFile": "tests/e2e/plugins/index.js" +} diff --git a/website/frontend/jest.config.js b/website/frontend/jest.config.js new file mode 100644 index 0000000..06e2d9f --- /dev/null +++ b/website/frontend/jest.config.js @@ -0,0 +1,49 @@ +module.exports = { + moduleFileExtensions: [ + 'js', + 'jsx', + 'json', + 'vue', + 'ts', + 'tsx' + ], + transform: { + '^.+\\.vue$': 'vue-jest', + '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub', + '^.+\\.tsx?$': 'ts-jest' + }, + transformIgnorePatterns: [ + '/node_modules/' + ], + moduleNameMapper: { + '^@/(.*)$': '/src/$1' + }, + snapshotSerializers: [ + 'jest-serializer-vue' + ], + testMatch: [ + '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' + ], + collectCoverage: true, + collectCoverageFrom: [ + 'src/utils/**/*.{ts,vue}', + '!src/utils/auth.ts', + '!src/utils/request.ts', + 'src/components/**/*.{ts,vue}' + ], + coverageDirectory: '/tests/unit/coverage', + coverageReporters: [ + 'lcov', + 'text-summary' + ], + testURL: 'http://localhost/', + watchPlugins: [ + 'jest-watch-typeahead/filename', + 'jest-watch-typeahead/testname' + ], + globals: { + 'ts-jest': { + babelConfig: true + } + } +} diff --git a/website/frontend/package-lock.json b/website/frontend/package-lock.json new file mode 100644 index 0000000..ff158b5 --- /dev/null +++ b/website/frontend/package-lock.json @@ -0,0 +1,20844 @@ +{ + "name": "vue-typescript-admin-template", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/core": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz", + "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.0", + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helpers": "^7.9.0", + "@babel/parser": "^7.9.0", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.9.0", + "@babel/types": "^7.9.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.5.tgz", + "integrity": "sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ==", + "dev": true, + "requires": { + "@babel/types": "^7.9.5", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz", + "integrity": "sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz", + "integrity": "sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.9.5.tgz", + "integrity": "sha512-IipaxGaQmW4TfWoXdqjY0TzoXQ1HRS0kPpEgvjosb3u7Uedcq297xFqDQiCcQtRRwzIMif+N1MLVI8C5a4/PAA==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.9.5", + "@babel/helper-member-expression-to-functions": "^7.8.3", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", + "@babel/helper-split-export-declaration": "^7.8.3" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.8.8", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz", + "integrity": "sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-regex": "^7.8.3", + "regexpu-core": "^4.7.0" + } + }, + "@babel/helper-define-map": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz", + "integrity": "sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.8.3", + "@babel/types": "^7.8.3", + "lodash": "^4.17.13" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz", + "integrity": "sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw==", + "dev": true, + "requires": { + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-function-name": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz", + "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.9.5" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz", + "integrity": "sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", + "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-module-imports": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", + "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-module-transforms": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", + "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", + "@babel/helper-simple-access": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/template": "^7.8.6", + "@babel/types": "^7.9.0", + "lodash": "^4.17.13" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", + "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.8.3.tgz", + "integrity": "sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==", + "dev": true, + "requires": { + "lodash": "^4.17.13" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz", + "integrity": "sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-wrap-function": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-replace-supers": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz", + "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.8.3", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.6" + } + }, + "@babel/helper-simple-access": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", + "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", + "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz", + "integrity": "sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helpers": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.2.tgz", + "integrity": "sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.9.0", + "@babel/types": "^7.9.0" + } + }, + "@babel/highlight": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", + "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.9.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz", + "integrity": "sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-remap-async-to-generator": "^7.8.3", + "@babel/plugin-syntax-async-generators": "^7.8.0" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz", + "integrity": "sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-proposal-decorators": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.8.3.tgz", + "integrity": "sha512-e3RvdvS4qPJVTe288DlXjwKflpfy1hr0j5dz5WpIYYeP7vQZg2WfAEIp8k5/Lwis/m5REXEteIz6rrcDtXXG7w==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-decorators": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz", + "integrity": "sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.0" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.5.tgz", + "integrity": "sha512-VP2oXvAf7KCYTthbUHwBlewbl1Iq059f6seJGsxMizaCdgHIeczOr7FBqELhSqfkIl04Fi8okzWzl63UKbQmmg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.9.5" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.8.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz", + "integrity": "sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.8.8", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.8.3.tgz", + "integrity": "sha512-8Hg4dNNT9/LcA1zQlfwuKR8BUc/if7Q7NkTam9sGTcJphLwpf2g4S42uhspQrIrR+dpzE0dtTqBVFoHl8GtnnQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.8.3.tgz", + "integrity": "sha512-WxdW9xyLgBdefoo0Ynn3MRSkhe5tFVxxKNVdnZSh318WrG2e2jH+E9wd/++JsqcLJZPfz87njQJ8j2Upjm0M0A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz", + "integrity": "sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz", + "integrity": "sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-remap-async-to-generator": "^7.8.3" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz", + "integrity": "sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz", + "integrity": "sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "lodash": "^4.17.13" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz", + "integrity": "sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-define-map": "^7.8.3", + "@babel/helper-function-name": "^7.9.5", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", + "@babel/helper-split-export-declaration": "^7.8.3", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz", + "integrity": "sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.9.5.tgz", + "integrity": "sha512-j3OEsGel8nHL/iusv/mRd5fYZ3DrOxWC82x0ogmdN/vHfAP4MYw+AFKYanzWlktNwikKvlzUV//afBW5FTp17Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz", + "integrity": "sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz", + "integrity": "sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz", + "integrity": "sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz", + "integrity": "sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz", + "integrity": "sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz", + "integrity": "sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.0.tgz", + "integrity": "sha512-vZgDDF003B14O8zJy0XXLnPH4sg+9X5hFBBGN1V+B2rgrB+J2xIypSN6Rk9imB2hSTHQi5OHLrFWsZab1GMk+Q==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3", + "babel-plugin-dynamic-import-node": "^2.3.0" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.0.tgz", + "integrity": "sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-simple-access": "^7.8.3", + "babel-plugin-dynamic-import-node": "^2.3.0" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.0.tgz", + "integrity": "sha512-FsiAv/nao/ud2ZWy4wFacoLOm5uxl0ExSQ7ErvP7jpoihLR6Cq90ilOFyX9UXct3rbtKsAiZ9kFt5XGfPe/5SQ==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.8.3", + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3", + "babel-plugin-dynamic-import-node": "^2.3.0" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz", + "integrity": "sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz", + "integrity": "sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.8.3" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz", + "integrity": "sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz", + "integrity": "sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.3" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz", + "integrity": "sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz", + "integrity": "sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.0.tgz", + "integrity": "sha512-pUu9VSf3kI1OqbWINQ7MaugnitRss1z533436waNXp+0N3ur3zfut37sXiQMxkuCF4VUjwZucen/quskCh7NHw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "resolve": "^1.8.1", + "semver": "^5.5.1" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz", + "integrity": "sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz", + "integrity": "sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz", + "integrity": "sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-regex": "^7.8.3" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz", + "integrity": "sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz", + "integrity": "sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz", + "integrity": "sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/preset-env": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.3.4.tgz", + "integrity": "sha512-2mwqfYMK8weA0g0uBKOt4FE3iEodiHy9/CW0b+nWXcbL+pGzLx8ESYc+j9IIxr6LTDHWKgPm71i9smo02bw+gA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-async-generator-functions": "^7.2.0", + "@babel/plugin-proposal-json-strings": "^7.2.0", + "@babel/plugin-proposal-object-rest-spread": "^7.3.4", + "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.2.0", + "@babel/plugin-syntax-async-generators": "^7.2.0", + "@babel/plugin-syntax-json-strings": "^7.2.0", + "@babel/plugin-syntax-object-rest-spread": "^7.2.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", + "@babel/plugin-transform-arrow-functions": "^7.2.0", + "@babel/plugin-transform-async-to-generator": "^7.3.4", + "@babel/plugin-transform-block-scoped-functions": "^7.2.0", + "@babel/plugin-transform-block-scoping": "^7.3.4", + "@babel/plugin-transform-classes": "^7.3.4", + "@babel/plugin-transform-computed-properties": "^7.2.0", + "@babel/plugin-transform-destructuring": "^7.2.0", + "@babel/plugin-transform-dotall-regex": "^7.2.0", + "@babel/plugin-transform-duplicate-keys": "^7.2.0", + "@babel/plugin-transform-exponentiation-operator": "^7.2.0", + "@babel/plugin-transform-for-of": "^7.2.0", + "@babel/plugin-transform-function-name": "^7.2.0", + "@babel/plugin-transform-literals": "^7.2.0", + "@babel/plugin-transform-modules-amd": "^7.2.0", + "@babel/plugin-transform-modules-commonjs": "^7.2.0", + "@babel/plugin-transform-modules-systemjs": "^7.3.4", + "@babel/plugin-transform-modules-umd": "^7.2.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.3.0", + "@babel/plugin-transform-new-target": "^7.0.0", + "@babel/plugin-transform-object-super": "^7.2.0", + "@babel/plugin-transform-parameters": "^7.2.0", + "@babel/plugin-transform-regenerator": "^7.3.4", + "@babel/plugin-transform-shorthand-properties": "^7.2.0", + "@babel/plugin-transform-spread": "^7.2.0", + "@babel/plugin-transform-sticky-regex": "^7.2.0", + "@babel/plugin-transform-template-literals": "^7.2.0", + "@babel/plugin-transform-typeof-symbol": "^7.2.0", + "@babel/plugin-transform-unicode-regex": "^7.2.0", + "browserslist": "^4.3.4", + "invariant": "^2.2.2", + "js-levenshtein": "^1.1.3", + "semver": "^5.3.0" + } + }, + "@babel/runtime": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz", + "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", + "dev": true + } + } + }, + "@babel/runtime-corejs2": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.9.2.tgz", + "integrity": "sha512-ayjSOxuK2GaSDJFCtLgHnYjuMyIpViNujWrZo8GUpN60/n7juzJKK5yOo6RFVb0zdU9ACJFK+MsZrUnj3OmXMw==", + "dev": true, + "requires": { + "core-js": "^2.6.5", + "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", + "dev": true + } + } + }, + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + } + }, + "@babel/traverse": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.5.tgz", + "integrity": "sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.5", + "@babel/helper-function-name": "^7.9.5", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.9.0", + "@babel/types": "^7.9.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.5.tgz", + "integrity": "sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.9.5", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@cypress/listr-verbose-renderer": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", + "integrity": "sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-cursor": "^1.0.2", + "date-fns": "^1.27.2", + "figures": "^1.7.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "@cypress/xvfb": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", + "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "lodash.once": "^4.1.1" + } + }, + "@hapi/address": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", + "integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==", + "dev": true + }, + "@hapi/bourne": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-1.3.2.tgz", + "integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==", + "dev": true + }, + "@hapi/hoek": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.5.1.tgz", + "integrity": "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==", + "dev": true + }, + "@hapi/joi": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.1.1.tgz", + "integrity": "sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==", + "dev": true, + "requires": { + "@hapi/address": "2.x.x", + "@hapi/bourne": "1.x.x", + "@hapi/hoek": "8.x.x", + "@hapi/topo": "3.x.x" + } + }, + "@hapi/topo": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.6.tgz", + "integrity": "sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==", + "dev": true, + "requires": { + "@hapi/hoek": "^8.3.0" + } + }, + "@intervolga/optimize-cssnano-plugin": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@intervolga/optimize-cssnano-plugin/-/optimize-cssnano-plugin-1.0.6.tgz", + "integrity": "sha512-zN69TnSr0viRSU6cEDIcuPcP67QcpQ6uHACg58FiN9PDrU6SLyGW3MR4tiISbYxy1kDWAVPwD+XwQTWE5cigAA==", + "dev": true, + "requires": { + "cssnano": "^4.0.0", + "cssnano-preset-default": "^4.0.0", + "postcss": "^7.0.0" + } + }, + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/core": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.9.0.tgz", + "integrity": "sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/reporters": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-changed-files": "^24.9.0", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-resolve-dependencies": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "jest-watcher": "^24.9.0", + "micromatch": "^3.1.10", + "p-each-series": "^1.0.0", + "realpath-native": "^1.1.0", + "rimraf": "^2.5.4", + "slash": "^2.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "babel-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", + "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", + "dev": true, + "requires": { + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.9.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", + "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + } + }, + "babel-plugin-jest-hoist": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", + "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", + "dev": true, + "requires": { + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", + "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", + "dev": true, + "requires": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.9.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true + }, + "expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + } + }, + "jest-changed-files": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", + "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "execa": "^1.0.0", + "throat": "^4.0.0" + } + }, + "jest-config": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.9.0", + "realpath-native": "^1.1.0" + } + }, + "jest-docblock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", + "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", + "dev": true, + "requires": { + "detect-newline": "^2.1.0" + } + }, + "jest-each": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", + "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-environment-jsdom": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", + "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0", + "jsdom": "^11.5.1" + } + }, + "jest-environment-node": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", + "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0" + } + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", + "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.9.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0", + "throat": "^4.0.0" + } + }, + "jest-leak-detector": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", + "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", + "dev": true, + "requires": { + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + }, + "jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-resolve-dependencies": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz", + "integrity": "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-snapshot": "^24.9.0" + } + }, + "jest-runner": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", + "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.4.2", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-docblock": "^24.3.0", + "jest-haste-map": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-leak-detector": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "source-map-support": "^0.5.6", + "throat": "^4.0.0" + } + }, + "jest-runtime": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", + "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^13.3.0" + } + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-snapshot": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.9.0", + "semver": "^6.2.0" + } + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + } + }, + "jest-validate": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", + "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "camelcase": "^5.3.1", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "leven": "^3.1.0", + "pretty-format": "^24.9.0" + } + }, + "jest-watcher": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", + "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", + "dev": true, + "requires": { + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "jest-util": "^24.9.0", + "string-length": "^2.0.0" + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "@jest/environment": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", + "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "dev": true, + "requires": { + "@jest/fake-timers": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0" + }, + "dependencies": { + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + } + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + }, + "dependencies": { + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + } + } + }, + "@jest/reporters": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.9.0.tgz", + "integrity": "sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.2", + "istanbul-lib-coverage": "^2.0.2", + "istanbul-lib-instrument": "^3.0.1", + "istanbul-lib-report": "^2.0.4", + "istanbul-lib-source-maps": "^3.0.1", + "istanbul-reports": "^2.2.6", + "jest-haste-map": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "node-notifier": "^5.4.2", + "slash": "^2.0.0", + "source-map": "^0.6.0", + "string-length": "^2.0.0" + }, + "dependencies": { + "@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "babel-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", + "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", + "dev": true, + "requires": { + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.9.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", + "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + } + }, + "babel-plugin-jest-hoist": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", + "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", + "dev": true, + "requires": { + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", + "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", + "dev": true, + "requires": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.9.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true + }, + "expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + } + }, + "istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", + "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0" + } + }, + "jest-config": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.9.0", + "realpath-native": "^1.1.0" + } + }, + "jest-each": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", + "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-environment-jsdom": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", + "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0", + "jsdom": "^11.5.1" + } + }, + "jest-environment-node": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", + "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0" + } + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", + "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.9.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0", + "throat": "^4.0.0" + } + }, + "jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + }, + "jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-runtime": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", + "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^13.3.0" + } + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-snapshot": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.9.0", + "semver": "^6.2.0" + } + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + } + }, + "jest-validate": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", + "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "camelcase": "^5.3.1", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "leven": "^3.1.0", + "pretty-format": "^24.9.0" + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + } + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/test-sequencer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz", + "integrity": "sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A==", + "dev": true, + "requires": { + "@jest/test-result": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0" + }, + "dependencies": { + "@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "babel-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", + "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", + "dev": true, + "requires": { + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.9.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", + "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + } + }, + "babel-plugin-jest-hoist": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", + "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", + "dev": true, + "requires": { + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", + "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", + "dev": true, + "requires": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.9.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true + }, + "expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + } + }, + "jest-config": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.9.0", + "realpath-native": "^1.1.0" + } + }, + "jest-docblock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", + "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", + "dev": true, + "requires": { + "detect-newline": "^2.1.0" + } + }, + "jest-each": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", + "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-environment-jsdom": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", + "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0", + "jsdom": "^11.5.1" + } + }, + "jest-environment-node": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", + "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0" + } + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", + "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.9.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0", + "throat": "^4.0.0" + } + }, + "jest-leak-detector": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", + "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", + "dev": true, + "requires": { + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + }, + "jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-runner": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", + "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.4.2", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-docblock": "^24.3.0", + "jest-haste-map": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-leak-detector": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "source-map-support": "^0.5.6", + "throat": "^4.0.0" + } + }, + "jest-runtime": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", + "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^13.3.0" + } + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-snapshot": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.9.0", + "semver": "^6.2.0" + } + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + } + }, + "jest-validate": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", + "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "camelcase": "^5.3.1", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "leven": "^3.1.0", + "pretty-format": "^24.9.0" + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "@jest/transform": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.9.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", + "micromatch": "^3.1.10", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + }, + "dependencies": { + "@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "babel-plugin-istanbul": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", + "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + } + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + } + }, + "write-file-atomic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", + "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + } + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true + }, + "@soda/friendly-errors-webpack-plugin": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.1.tgz", + "integrity": "sha512-cWKrGaFX+rfbMrAxVv56DzhPNqOJPZuNIS2HGMELtgGzb+vsMzyig9mml5gZ/hr2BGtSLV+dP2LUEuAL8aG2mQ==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "error-stack-parser": "^2.0.0", + "string-width": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "@types/babel__core": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.7.tgz", + "integrity": "sha512-RL62NqSFPCDK2FM1pSDH0scHpJvsXtZNiYlMB73DgPBaG1E38ZYVL+ei5EkWRbr+KC4YNiAUNBnRj+bgwpgjMw==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz", + "integrity": "sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", + "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.10.tgz", + "integrity": "sha512-74fNdUGrWsgIB/V9kTO5FGHPWYY6Eqn+3Z7L6Hc4e/BxjYV7puvBqp5HwsVYYfLm6iURYBNCx4Ut37OF9yitCw==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, + "@types/events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", + "dev": true + }, + "@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", + "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz", + "integrity": "sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "24.9.1", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.9.1.tgz", + "integrity": "sha512-Fb38HkXSVA4L8fGKEZ6le5bB8r6MRWlOCZbVuWZcmOMSCd2wCYOwN1ibj8daIoV9naq7aaOZjrLCoCMptKU/4Q==", + "dev": true, + "requires": { + "jest-diff": "^24.3.0" + } + }, + "@types/js-cookie": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.6.tgz", + "integrity": "sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", + "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/node": { + "version": "12.12.35", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.35.tgz", + "integrity": "sha512-ASYsaKecA7TUsDrqIGPNk3JeEox0z/0XR/WsJJ8BIX/9+SkMSImQXKWfU/yBrSyc7ZSE/NPqLu36Nur0miCFfQ==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "@types/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@types/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==", + "dev": true + }, + "@types/q": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz", + "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==" + }, + "@types/sizzle": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz", + "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==", + "dev": true + }, + "@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", + "dev": true + }, + "@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", + "dev": true + }, + "@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "dev": true + }, + "@types/webpack-env": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.15.1.tgz", + "integrity": "sha512-eWN5ElDTeBc5lRDh95SqA8x18D0ll2pWudU3uWiyfsRmIZcmUXpEsxPU+7+BsdCrO2vfLRC629u/MmjbmF+2tA==", + "dev": true + }, + "@types/yargs": { + "version": "13.0.8", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.8.tgz", + "integrity": "sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.13.0.tgz", + "integrity": "sha512-WQHCozMnuNADiqMtsNzp96FNox5sOVpU8Xt4meaT4em8lOG1SrOv92/mUbEHQVh90sldKSfcOc/I0FOb/14G1g==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "1.13.0", + "eslint-utils": "^1.3.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^2.0.1", + "tsutils": "^3.7.0" + }, + "dependencies": { + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz", + "integrity": "sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "1.13.0", + "eslint-scope": "^4.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.13.0.tgz", + "integrity": "sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "1.13.0", + "@typescript-eslint/typescript-estree": "1.13.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz", + "integrity": "sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==", + "dev": true, + "requires": { + "lodash.unescape": "4.0.1", + "semver": "5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + } + } + }, + "@vue/babel-helper-vue-jsx-merge-props": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.0.0.tgz", + "integrity": "sha512-6tyf5Cqm4m6v7buITuwS+jHzPlIPxbFzEhXR5JGZpbrvOcp1hiQKckd305/3C7C36wFekNTQSxAtgeM0j0yoUw==", + "dev": true + }, + "@vue/babel-plugin-transform-vue-jsx": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.1.2.tgz", + "integrity": "sha512-YfdaoSMvD1nj7+DsrwfTvTnhDXI7bsuh+Y5qWwvQXlD24uLgnsoww3qbiZvWf/EoviZMrvqkqN4CBw0W3BWUTQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0", + "html-tags": "^2.0.0", + "lodash.kebabcase": "^4.1.1", + "svg-tags": "^1.0.0" + } + }, + "@vue/babel-preset-app": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/@vue/babel-preset-app/-/babel-preset-app-3.12.1.tgz", + "integrity": "sha512-Zjy5jQaikV1Pz+ri0YgXFS7q4/5wCxB5tRkDOEIt5+4105u0Feb/pvH20nVL6nx9GyXrECFfcm7Yxr/z++OaPQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/plugin-proposal-class-properties": "^7.0.0", + "@babel/plugin-proposal-decorators": "^7.1.0", + "@babel/plugin-syntax-dynamic-import": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.0.0", + "@babel/plugin-transform-runtime": "^7.4.0", + "@babel/preset-env": "^7.0.0 < 7.4.0", + "@babel/runtime": "^7.0.0", + "@babel/runtime-corejs2": "^7.2.0", + "@vue/babel-preset-jsx": "^1.0.0", + "babel-plugin-dynamic-import-node": "^2.2.0", + "babel-plugin-module-resolver": "3.2.0", + "core-js": "^2.6.5" + } + }, + "@vue/babel-preset-jsx": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-preset-jsx/-/babel-preset-jsx-1.1.2.tgz", + "integrity": "sha512-zDpVnFpeC9YXmvGIDSsKNdL7qCG2rA3gjywLYHPCKDT10erjxF4U+6ay9X6TW5fl4GsDlJp9bVfAVQAAVzxxvQ==", + "dev": true, + "requires": { + "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.1.2", + "@vue/babel-sugar-functional-vue": "^1.1.2", + "@vue/babel-sugar-inject-h": "^1.1.2", + "@vue/babel-sugar-v-model": "^1.1.2", + "@vue/babel-sugar-v-on": "^1.1.2" + } + }, + "@vue/babel-sugar-functional-vue": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.1.2.tgz", + "integrity": "sha512-YhmdJQSVEFF5ETJXzrMpj0nkCXEa39TvVxJTuVjzvP2rgKhdMmQzlJuMv/HpadhZaRVMCCF3AEjjJcK5q/cYzQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0" + } + }, + "@vue/babel-sugar-inject-h": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.1.2.tgz", + "integrity": "sha512-VRSENdTvD5htpnVp7i7DNuChR5rVMcORdXjvv5HVvpdKHzDZAYiLSD+GhnhxLm3/dMuk8pSzV+k28ECkiN5m8w==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0" + } + }, + "@vue/babel-sugar-v-model": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.1.2.tgz", + "integrity": "sha512-vLXPvNq8vDtt0u9LqFdpGM9W9IWDmCmCyJXuozlq4F4UYVleXJ2Fa+3JsnTZNJcG+pLjjfnEGHci2339Kj5sGg==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.1.2", + "camelcase": "^5.0.0", + "html-tags": "^2.0.0", + "svg-tags": "^1.0.0" + } + }, + "@vue/babel-sugar-v-on": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.1.2.tgz", + "integrity": "sha512-T8ZCwC8Jp2uRtcZ88YwZtZXe7eQrJcfRq0uTFy6ShbwYJyz5qWskRFoVsdTi9o0WEhmQXxhQUewodOSCUPVmsQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.1.2", + "camelcase": "^5.0.0" + } + }, + "@vue/cli-overlay": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/@vue/cli-overlay/-/cli-overlay-3.12.1.tgz", + "integrity": "sha512-Bym92EN+lj+cNRN2ozbYyH+V8DMXWGbCDUk+hiJ4EYDBZfBkZKvalk1/mOBFwyxiopnnbOEBAAhL/UuMQ1xARg==", + "dev": true + }, + "@vue/cli-plugin-babel": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-babel/-/cli-plugin-babel-3.12.1.tgz", + "integrity": "sha512-Zetvz8PikLCGomeKOKu8pC9YQ7cfxs7pGpvEOzaxGdhMnebhjAYR6i6dOB57A6N5lhxQksXCtYTv26QgfiIpdg==", + "dev": true, + "requires": { + "@babel/core": "^7.0.0", + "@vue/babel-preset-app": "^3.12.1", + "@vue/cli-shared-utils": "^3.12.1", + "babel-loader": "^8.0.5", + "webpack": "^4.0.0" + } + }, + "@vue/cli-plugin-e2e-cypress": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-e2e-cypress/-/cli-plugin-e2e-cypress-3.12.1.tgz", + "integrity": "sha512-2BYYhXTkT9+rXKv8OfvpuEbX7KYo0Ma4vNTjFG62W+CkjO+R10mYwJvRMaXUFaV6iRrkKQab5oCuNMCnefqVKg==", + "dev": true, + "requires": { + "@vue/cli-shared-utils": "^3.12.1", + "cypress": "^3.2.0", + "eslint-plugin-cypress": "^2.2.1" + } + }, + "@vue/cli-plugin-eslint": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-eslint/-/cli-plugin-eslint-3.12.1.tgz", + "integrity": "sha512-tVTZlEZsy3sQbO4LLWFK11yzlWwqVAqaM+IY+BeWHITBzEJKh2KmouG+x6x/reXiU3qROsMJ4Ej3Hs8buSMWyQ==", + "dev": true, + "requires": { + "@vue/cli-shared-utils": "^3.12.1", + "babel-eslint": "^10.0.1", + "eslint": "^4.19.1", + "eslint-loader": "^2.1.2", + "eslint-plugin-vue": "^4.7.1", + "globby": "^9.2.0", + "webpack": "^4.0.0", + "yorkie": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "optional": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "optional": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "eslint": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", + "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", + "dev": true, + "optional": true, + "requires": { + "ajv": "^5.3.0", + "babel-code-frame": "^6.22.0", + "chalk": "^2.1.0", + "concat-stream": "^1.6.0", + "cross-spawn": "^5.1.0", + "debug": "^3.1.0", + "doctrine": "^2.1.0", + "eslint-scope": "^3.7.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^3.5.4", + "esquery": "^1.0.0", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.0.1", + "ignore": "^3.3.3", + "imurmurhash": "^0.1.4", + "inquirer": "^3.0.6", + "is-resolvable": "^1.0.0", + "js-yaml": "^3.9.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.4", + "minimatch": "^3.0.2", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "pluralize": "^7.0.0", + "progress": "^2.0.0", + "regexpp": "^1.0.1", + "require-uncached": "^1.0.3", + "semver": "^5.3.0", + "strip-ansi": "^4.0.0", + "strip-json-comments": "~2.0.1", + "table": "4.0.2", + "text-table": "~0.2.0" + } + }, + "eslint-plugin-vue": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-4.7.1.tgz", + "integrity": "sha512-esETKhVMI7Vdli70Wt4bvAwnZBJeM0pxVX9Yb0wWKxdCJc2EADalVYK/q2FzMw8oKN0wPMdqVCKS8kmR89recA==", + "dev": true, + "optional": true, + "requires": { + "vue-eslint-parser": "^2.0.3" + } + }, + "eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", + "dev": true, + "optional": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true, + "optional": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true, + "optional": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "optional": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true, + "optional": true + } + } + }, + "@vue/cli-plugin-pwa": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-pwa/-/cli-plugin-pwa-3.12.1.tgz", + "integrity": "sha512-ver9mJ1t4gqXeB4CmTAYYqnqx8zDzo6ORwWK+iMTvNI2aOrJReh7QTdtWpQbkqHSzN4jNUtYSLIsP/ONdrQOhw==", + "dev": true, + "requires": { + "@vue/cli-shared-utils": "^3.12.1", + "webpack": "^4.0.0", + "workbox-webpack-plugin": "^3.6.3" + } + }, + "@vue/cli-plugin-typescript": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-typescript/-/cli-plugin-typescript-3.12.1.tgz", + "integrity": "sha512-sh+WKbpsDw6wOrpM4FSD1xKXpyp8mVcl+yyEk+WvJuuSdfwueRubAM7uYbrOGtNSOegpZqBwbNxEO4FIUBeLKQ==", + "dev": true, + "requires": { + "@types/webpack-env": "^1.13.9", + "@vue/cli-shared-utils": "^3.12.1", + "fork-ts-checker-webpack-plugin": "^0.5.2", + "globby": "^9.2.0", + "ts-loader": "^5.3.3", + "tslint": "^5.15.0", + "webpack": "^4.0.0", + "yorkie": "^2.0.0" + } + }, + "@vue/cli-plugin-unit-jest": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-unit-jest/-/cli-plugin-unit-jest-3.12.1.tgz", + "integrity": "sha512-Cc9Kq4+RaUN1yfNVb7c9hVDNXo2tFTWHgwooCL3XWMu2iL+pDawQt8ZeSqauDY95JoMeEAVy2xBimjL+7jo/jw==", + "dev": true, + "requires": { + "@vue/cli-shared-utils": "^3.12.1", + "babel-jest": "^23.6.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", + "jest": "^23.6.0", + "jest-serializer-vue": "^2.0.2", + "jest-transform-stub": "^2.0.0", + "jest-watch-typeahead": "0.2.1", + "vue-jest": "^3.0.4" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "jest": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-23.6.0.tgz", + "integrity": "sha512-lWzcd+HSiqeuxyhG+EnZds6iO3Y3ZEnMrfZq/OTGvF/C+Z4fPMCdhWTGSAiO2Oym9rbEXfwddHhh6jqrTF3+Lw==", + "dev": true, + "requires": { + "import-local": "^1.0.0", + "jest-cli": "^23.6.0" + }, + "dependencies": { + "jest-cli": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-23.6.0.tgz", + "integrity": "sha512-hgeD1zRUp1E1zsiyOXjEn4LzRLWdJBV//ukAHGlx6s5mfCNJTbhbHjgxnDUXA8fsKWN/HqFFF6X5XcCwC/IvYQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "import-local": "^1.0.0", + "is-ci": "^1.0.10", + "istanbul-api": "^1.3.1", + "istanbul-lib-coverage": "^1.2.0", + "istanbul-lib-instrument": "^1.10.1", + "istanbul-lib-source-maps": "^1.2.4", + "jest-changed-files": "^23.4.2", + "jest-config": "^23.6.0", + "jest-environment-jsdom": "^23.4.0", + "jest-get-type": "^22.1.0", + "jest-haste-map": "^23.6.0", + "jest-message-util": "^23.4.0", + "jest-regex-util": "^23.3.0", + "jest-resolve-dependencies": "^23.6.0", + "jest-runner": "^23.6.0", + "jest-runtime": "^23.6.0", + "jest-snapshot": "^23.6.0", + "jest-util": "^23.4.0", + "jest-validate": "^23.6.0", + "jest-watcher": "^23.4.0", + "jest-worker": "^23.2.0", + "micromatch": "^2.3.11", + "node-notifier": "^5.2.1", + "prompts": "^0.1.9", + "realpath-native": "^1.0.0", + "rimraf": "^2.5.4", + "slash": "^1.0.0", + "string-length": "^2.0.0", + "strip-ansi": "^4.0.0", + "which": "^1.2.12", + "yargs": "^11.0.0" + } + } + } + }, + "jest-get-type": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", + "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.1.tgz", + "integrity": "sha512-PRU7gJrJaXv3q3yQZ/+/X6KBswZiaQ+zOmdprZcouPYtQgvNU35i+68M4b1ZHLZtYFT5QObFLV+ZkmJYcwKdiw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" + } + }, + "yargs-parser": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", + "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "@vue/cli-service": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-3.12.1.tgz", + "integrity": "sha512-PDxNrTGnSKzeV1ruFlsRIAO8JcPizwT0EJXq9GeyooU+p+sOkv7aKkCBJQVYNjZapD1NOGWx6CvAAC/wAW+gew==", + "dev": true, + "requires": { + "@intervolga/optimize-cssnano-plugin": "^1.0.5", + "@soda/friendly-errors-webpack-plugin": "^1.7.1", + "@vue/cli-overlay": "^3.12.1", + "@vue/cli-shared-utils": "^3.12.1", + "@vue/component-compiler-utils": "^3.0.0", + "@vue/preload-webpack-plugin": "^1.1.0", + "@vue/web-component-wrapper": "^1.2.0", + "acorn": "^6.1.1", + "acorn-walk": "^6.1.1", + "address": "^1.0.3", + "autoprefixer": "^9.5.1", + "browserslist": "^4.5.4", + "cache-loader": "^2.0.1", + "case-sensitive-paths-webpack-plugin": "^2.2.0", + "chalk": "^2.4.2", + "cli-highlight": "^2.1.0", + "clipboardy": "^2.0.0", + "cliui": "^5.0.0", + "copy-webpack-plugin": "^4.6.0", + "css-loader": "^1.0.1", + "cssnano": "^4.1.10", + "current-script-polyfill": "^1.0.0", + "debug": "^4.1.1", + "default-gateway": "^5.0.2", + "dotenv": "^7.0.0", + "dotenv-expand": "^5.1.0", + "escape-string-regexp": "^1.0.5", + "file-loader": "^3.0.1", + "fs-extra": "^7.0.1", + "globby": "^9.2.0", + "hash-sum": "^1.0.2", + "html-webpack-plugin": "^3.2.0", + "launch-editor-middleware": "^2.2.1", + "lodash.defaultsdeep": "^4.6.1", + "lodash.mapvalues": "^4.6.0", + "lodash.transform": "^4.6.0", + "mini-css-extract-plugin": "^0.8.0", + "minimist": "^1.2.0", + "ora": "^3.4.0", + "portfinder": "^1.0.20", + "postcss-loader": "^3.0.0", + "read-pkg": "^5.0.0", + "semver": "^6.0.0", + "slash": "^2.0.0", + "source-map-url": "^0.4.0", + "ssri": "^6.0.1", + "string.prototype.padend": "^3.0.0", + "terser-webpack-plugin": "^1.2.3", + "thread-loader": "^2.1.2", + "url-loader": "^1.1.2", + "vue-loader": "^15.7.0", + "webpack": "^4.0.0", + "webpack-bundle-analyzer": "^3.3.0", + "webpack-chain": "^4.11.0", + "webpack-dev-server": "^3.4.1", + "webpack-merge": "^4.2.1" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "parse-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", + "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1", + "lines-and-columns": "^1.1.6" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + } + } + }, + "@vue/cli-shared-utils": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-3.12.1.tgz", + "integrity": "sha512-jFblzRFjutGwu5utOKdVlPlsbA1lBUNNQlAThzNqej+JtTKJjnvjlhjKX0Gq0oOny5FjKWhoyfQ74p9h1qE6JQ==", + "dev": true, + "requires": { + "@hapi/joi": "^15.0.1", + "chalk": "^2.4.1", + "execa": "^1.0.0", + "launch-editor": "^2.2.1", + "lru-cache": "^5.1.1", + "node-ipc": "^9.1.1", + "open": "^6.3.0", + "ora": "^3.4.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.7", + "semver": "^6.0.0", + "string.prototype.padstart": "^3.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@vue/component-compiler-utils": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.1.2.tgz", + "integrity": "sha512-QLq9z8m79mCinpaEeSURhnNCN6djxpHw0lpP/bodMlt5kALfONpryMthvnrQOlTcIKoF+VoPi+lPHUYeDFPXug==", + "dev": true, + "requires": { + "consolidate": "^0.15.1", + "hash-sum": "^1.0.2", + "lru-cache": "^4.1.2", + "merge-source-map": "^1.1.0", + "postcss": "^7.0.14", + "postcss-selector-parser": "^6.0.2", + "prettier": "^1.18.2", + "source-map": "~0.6.1", + "vue-template-es2015-compiler": "^1.9.0" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, + "@vue/eslint-config-standard": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-standard/-/eslint-config-standard-4.0.0.tgz", + "integrity": "sha512-bQghq1cw1BuMRHNhr3tRpAJx1tpGy0QtajQX873kLtA9YVuOIoXR7nAWnTN09bBHnSUh2N288vMsqPi2fI4Hzg==", + "dev": true, + "requires": { + "eslint-config-standard": "^12.0.0", + "eslint-plugin-import": "^2.14.0", + "eslint-plugin-node": "^8.0.0", + "eslint-plugin-promise": "^4.0.1", + "eslint-plugin-standard": "^4.0.0" + } + }, + "@vue/eslint-config-typescript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-4.0.0.tgz", + "integrity": "sha512-uSMAMgw4xDgVdZQhpbtJRo8nMV4oOy3Ht8olfOo7xvYFYLMF2JZ1tDRKd9/NSusxA72O2Vma+HzmyzDHg9evcQ==", + "dev": true, + "requires": { + "@typescript-eslint/eslint-plugin": "^1.1.0", + "@typescript-eslint/parser": "^1.1.0" + } + }, + "@vue/preload-webpack-plugin": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.1.tgz", + "integrity": "sha512-8VCoJeeH8tCkzhkpfOkt+abALQkS11OIHhte5MBzYaKMTqK0A3ZAKEUVAffsOklhEv7t0yrQt696Opnu9oAx+w==", + "dev": true + }, + "@vue/test-utils": { + "version": "1.0.0-beta.33", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-1.0.0-beta.33.tgz", + "integrity": "sha512-Xzqoe0lTLn3QRWfjhmKPOXYR86l0Y+g/zPHaheJQOkPLj5ojJl3rG0t4F3kXFWuLD88YzUVRMIBWOG7v9KOJQQ==", + "dev": true, + "requires": { + "dom-event-types": "^1.0.0", + "lodash": "^4.17.15", + "pretty": "^2.0.0" + } + }, + "@vue/web-component-wrapper": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vue/web-component-wrapper/-/web-component-wrapper-1.2.0.tgz", + "integrity": "sha512-Xn/+vdm9CjuC9p3Ae+lTClNutrVhsXpzxvoTXXtoys6kVRX9FkueSUAqSWAyZntmVLlR4DosBV4pH8y5Z/HbUw==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "abab": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", + "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", + "dev": true + }, + "acorn-globals": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", + "dev": true, + "requires": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + } + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "optional": true, + "requires": { + "acorn": "^3.0.4" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true, + "optional": true + } + } + }, + "acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", + "dev": true + }, + "address": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz", + "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==", + "dev": true + }, + "ajv": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", + "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true + }, + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "dev": true + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "dev": true + }, + "ansi-escapes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", + "dev": true + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "append-transform": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", + "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", + "dev": true, + "requires": { + "default-require-extensions": "^1.0.0" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "arch": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.1.tgz", + "integrity": "sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + } + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "array.prototype.flat": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", + "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "async-validator": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-1.8.5.tgz", + "integrity": "sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==", + "requires": { + "babel-runtime": "6.x" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "autoprefixer": { + "version": "9.7.6", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.6.tgz", + "integrity": "sha512-F7cYpbN7uVVhACZTeeIeealwdGM6wMtfWARVLTy5xmKtgVdBNJvbDRoCK3YO1orcs7gv/KwYlb3iXwu9Ug9BkQ==", + "dev": true, + "requires": { + "browserslist": "^4.11.1", + "caniuse-lite": "^1.0.30001039", + "chalk": "^2.4.2", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.27", + "postcss-value-parser": "^4.0.3" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", + "dev": true + }, + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "requires": { + "follow-redirects": "1.5.10" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "babel-core": { + "version": "7.0.0-bridge.0", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", + "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", + "dev": true + }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + } + }, + "babel-extract-comments": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/babel-extract-comments/-/babel-extract-comments-1.0.0.tgz", + "integrity": "sha512-qWWzi4TlddohA91bFwgt6zO/J0X+io7Qp184Fw0m2JYRSTZnJbFR8+07KmzudHCZgOiKRCrjhylwv9Xd8gfhVQ==", + "dev": true, + "requires": { + "babylon": "^6.18.0" + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + }, + "dependencies": { + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "babel-helper-vue-jsx-merge-props": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz", + "integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg==" + }, + "babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-jest": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-23.6.0.tgz", + "integrity": "sha512-lqKGG6LYXYu+DQh/slrQ8nxXQkEkhugdXsU6St7GmhVS7Ilc/22ArwqXNJrf0QaOBjZB0360qZMwXqDYQHXaew==", + "dev": true, + "requires": { + "babel-plugin-istanbul": "^4.1.6", + "babel-preset-jest": "^23.2.0" + } + }, + "babel-loader": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", + "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", + "dev": true, + "requires": { + "find-cache-dir": "^2.1.0", + "loader-utils": "^1.4.0", + "mkdirp": "^0.5.3", + "pify": "^4.0.1", + "schema-utils": "^2.6.5" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", + "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-istanbul": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz", + "integrity": "sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==", + "dev": true, + "requires": { + "babel-plugin-syntax-object-rest-spread": "^6.13.0", + "find-up": "^2.1.0", + "istanbul-lib-instrument": "^1.10.1", + "test-exclude": "^4.2.1" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + } + } + }, + "babel-plugin-jest-hoist": { + "version": "23.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.2.0.tgz", + "integrity": "sha1-5h+uBaHKiAGq3uV6bWa4zvr0QWc=", + "dev": true + }, + "babel-plugin-module-resolver": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-3.2.0.tgz", + "integrity": "sha512-tjR0GvSndzPew/Iayf4uICWZqjBwnlMWjSx6brryfQ81F9rxBVqwDJtFCV8oOs0+vJeefK9TmdZtkIFdFe1UnA==", + "dev": true, + "requires": { + "find-babel-config": "^1.1.0", + "glob": "^7.1.2", + "pkg-up": "^2.0.0", + "reselect": "^3.0.1", + "resolve": "^1.4.0" + } + }, + "babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", + "dev": true + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "dev": true, + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-object-rest-spread": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", + "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", + "dev": true, + "requires": { + "babel-plugin-syntax-object-rest-spread": "^6.8.0", + "babel-runtime": "^6.26.0" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-preset-jest": { + "version": "23.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz", + "integrity": "sha1-jsegOhOPABoaj7HoETZSvxpV2kY=", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^23.2.0", + "babel-plugin-syntax-object-rest-spread": "^6.13.0" + } + }, + "babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "dev": true, + "requires": { + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" + }, + "dependencies": { + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "^0.5.6" + } + } + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + } + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + }, + "dependencies": { + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + } + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bfj": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.2.tgz", + "integrity": "sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "check-types": "^8.0.3", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + }, + "dependencies": { + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + } + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "dev": true, + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } + } + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.11.1.tgz", + "integrity": "sha512-DCTr3kDrKEYNw6Jb9HFxVLQNaue8z+0ZfRBRjmCunKDEXEBajKDj2Y+Uelg+Pi29OnvaSGwjOsnRyNEkXzHg5g==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001038", + "electron-to-chromium": "^1.3.390", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" + } + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cache-loader": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cache-loader/-/cache-loader-2.0.1.tgz", + "integrity": "sha512-V99T3FOynmGx26Zom+JrVBytLBsmUCzVG2/4NnUKgvXN4bEV42R1ERl1IyiH/cvFIDA1Ytq2lPZ9tXDSahcQpQ==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "mkdirp": "^0.5.1", + "neo-async": "^2.6.0", + "normalize-path": "^3.0.0", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "cachedir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-1.3.0.tgz", + "integrity": "sha512-O1ji32oyON9laVPJL1IZ5bmwd2cB46VfpxkDequezH+15FDzzVddEyrGEeX4WusDSqKxdyFdDQDEG1yo1GoWkg==", + "dev": true, + "requires": { + "os-homedir": "^1.0.1" + } + }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", + "dev": true + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + }, + "dependencies": { + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + } + } + }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "optional": true, + "requires": { + "callsites": "^0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true, + "optional": true + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001040", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001040.tgz", + "integrity": "sha512-Ep0tEPeI5wCvmJNrXjE3etgfI+lkl1fTDU6Y3ZH1mhrjkPlVI9W4pcKbMo+BQLpEWKVYYp2EmYaRsqpPC3k7lQ==", + "dev": true + }, + "capture-exit": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-1.2.0.tgz", + "integrity": "sha1-HF/MSJ/QqwDU8ax64QcuMXP7q28=", + "dev": true, + "requires": { + "rsvp": "^3.3.3" + } + }, + "case-sensitive-paths-webpack-plugin": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.3.0.tgz", + "integrity": "sha512-/4YgnZS8y1UXXmC02xD5rRrBEu6T5ub+mQHLNRj0fzTRbgdBYhsNo2V5EqwgqrExjxsjtF/OpAKAMkKsxbD5XQ==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true, + "optional": true + }, + "check-more-types": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", + "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=", + "dev": true + }, + "check-types": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz", + "integrity": "sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==", + "dev": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true, + "optional": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-highlight": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.4.tgz", + "integrity": "sha512-s7Zofobm20qriqDoU9sXptQx0t2R9PEgac92mENNm7xaEe1hn71IIMsXMK+6encA6WRCWWxIGQbipr3q998tlQ==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "highlight.js": "^9.6.0", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^5.1.1", + "yargs": "^15.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "yargs": { + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", + "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.1" + } + }, + "yargs-parser": { + "version": "18.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz", + "integrity": "sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "cli-spinners": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.3.0.tgz", + "integrity": "sha512-Xs2Hf2nzrvJMFKimOR7YR0QwZ8fc0u98kdtwN1eNAZzNQgH3vK2pXzff6GJtKh7S5hoJ87ECiAiZFS2fb5Ii2w==", + "dev": true + }, + "cli-truncate": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", + "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", + "dev": true, + "requires": { + "slice-ansi": "0.0.4", + "string-width": "^1.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "clipboardy": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz", + "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==", + "dev": true, + "requires": { + "arch": "^2.1.1", + "execa": "^1.0.0", + "is-wsl": "^2.1.1" + }, + "dependencies": { + "is-wsl": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz", + "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==", + "dev": true + } + } + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "requires": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz", + "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==", + "dev": true, + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "common-tags": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", + "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "condense-newlines": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/condense-newlines/-/condense-newlines-0.2.1.tgz", + "integrity": "sha1-PemFVTE5R10yUCyDsC9gaE0kxV8=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-whitespace": "^0.3.0", + "kind-of": "^3.0.2" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "config-chain": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", + "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "dev": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "consolidate": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", + "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "dev": true, + "requires": { + "bluebird": "^3.1.1" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-webpack-plugin": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz", + "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==", + "dev": true, + "requires": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "globby": "^7.1.1", + "is-glob": "^4.0.0", + "loader-utils": "^1.1.0", + "minimatch": "^3.0.4", + "p-limit": "^1.0.0", + "serialize-javascript": "^1.4.0" + }, + "dependencies": { + "cacache": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "mississippi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", + "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "serialize-javascript": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", + "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "ssri": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", + "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.1" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, + "core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "dependencies": { + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + } + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + } + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true + }, + "css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "dev": true, + "requires": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + } + }, + "css-loader": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-1.0.1.tgz", + "integrity": "sha512-+ZHAZm/yqvJ2kDtPne3uX0C+Vr3Zn5jFn2N4HywtS5ujwvsVkyg0VArEXpl3BgczDA8anieki1FIzhchX4yrDw==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "css-selector-tokenizer": "^0.7.0", + "icss-utils": "^2.1.0", + "loader-utils": "^1.0.2", + "lodash": "^4.17.11", + "postcss": "^6.0.23", + "postcss-modules-extract-imports": "^1.2.0", + "postcss-modules-local-by-default": "^1.2.0", + "postcss-modules-scope": "^1.1.0", + "postcss-modules-values": "^1.3.0", + "postcss-value-parser": "^3.3.0", + "source-list-map": "^2.0.0" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "css-selector-tokenizer": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz", + "integrity": "sha512-yj856NGuAymN6r8bn8/Jl46pR+OC3eEvAhfGYDUe7YPtTPAYrSSw4oAniZ9Y8T5B92hjhwTBLUen0/vKPxf6pw==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2", + "regexpu-core": "^4.6.0" + } + }, + "css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + } + }, + "css-what": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.2.1.tgz", + "integrity": "sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw==" + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "cssnano": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", + "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.7", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "cssnano-preset-default": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", + "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", + "dev": true, + "requires": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.2", + "postcss-unique-selectors": "^4.0.1" + } + }, + "cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", + "dev": true + }, + "cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", + "dev": true + }, + "cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "dev": true + }, + "csso": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.0.3.tgz", + "integrity": "sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ==", + "requires": { + "css-tree": "1.0.0-alpha.39" + }, + "dependencies": { + "css-tree": { + "version": "1.0.0-alpha.39", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.39.tgz", + "integrity": "sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==", + "requires": { + "mdn-data": "2.0.6", + "source-map": "^0.6.1" + } + }, + "mdn-data": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz", + "integrity": "sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==" + } + } + }, + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "cssstyle": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "dev": true, + "requires": { + "cssom": "0.3.x" + } + }, + "current-script-polyfill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/current-script-polyfill/-/current-script-polyfill-1.0.0.tgz", + "integrity": "sha1-8xz35PPiGLBybnOMqSoC00iO9hU=", + "dev": true + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "cypress": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-3.8.3.tgz", + "integrity": "sha512-I9L/d+ilTPPA4vq3NC1OPKmw7jJIpMKNdyfR8t1EXYzYCjyqbc59migOm1YSse/VRbISLJ+QGb5k4Y3bz2lkYw==", + "dev": true, + "requires": { + "@cypress/listr-verbose-renderer": "0.4.1", + "@cypress/xvfb": "1.2.4", + "@types/sizzle": "2.3.2", + "arch": "2.1.1", + "bluebird": "3.5.0", + "cachedir": "1.3.0", + "chalk": "2.4.2", + "check-more-types": "2.24.0", + "commander": "2.15.1", + "common-tags": "1.8.0", + "debug": "3.2.6", + "eventemitter2": "4.1.2", + "execa": "0.10.0", + "executable": "4.1.1", + "extract-zip": "1.6.7", + "fs-extra": "5.0.0", + "getos": "3.1.1", + "is-ci": "1.2.1", + "is-installed-globally": "0.1.0", + "lazy-ass": "1.6.0", + "listr": "0.12.0", + "lodash": "4.17.15", + "log-symbols": "2.2.0", + "minimist": "1.2.0", + "moment": "2.24.0", + "ramda": "0.24.1", + "request": "2.88.0", + "request-progress": "3.0.0", + "supports-color": "5.5.0", + "tmp": "0.1.0", + "untildify": "3.0.3", + "url": "0.11.0", + "yauzl": "2.10.0" + }, + "dependencies": { + "bluebird": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=", + "dev": true + }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + } + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + }, + "dependencies": { + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } + } + }, + "date-fns": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", + "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", + "dev": true + }, + "de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "deepmerge": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", + "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==" + }, + "default-gateway": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-5.0.5.tgz", + "integrity": "sha512-z2RnruVmj8hVMmAnEJMTIJNijhKCDiGjbLP+BHJFOT7ld3Bo5qcIBpVYDniqhbMIIf+jZDlkP2MkPXiQy/DBLA==", + "dev": true, + "requires": { + "execa": "^3.3.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", + "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz", + "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "p-finally": "^2.0.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "p-finally": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", + "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "default-require-extensions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", + "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", + "dev": true, + "requires": { + "strip-bom": "^2.0.0" + } + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "dev": true + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "diff-sequences": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "requires": { + "path-type": "^3.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "optional": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "requires": { + "utila": "~0.4" + } + }, + "dom-event-types": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dom-event-types/-/dom-event-types-1.0.0.tgz", + "integrity": "sha512-2G2Vwi2zXTHBGqXHsJ4+ak/iP0N8Ar+G8a7LiD2oup5o4sQWytwqqrZu/O6hIMV0KMID2PL69OhpshLO0n7UJQ==", + "dev": true + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" + } + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "dev": true, + "requires": { + "webidl-conversions": "^4.0.2" + } + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "dot-prop": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + }, + "dependencies": { + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + } + } + }, + "dotenv": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz", + "integrity": "sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==", + "dev": true + }, + "dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "easy-stack": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz", + "integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "editorconfig": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", + "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==", + "dev": true, + "requires": { + "commander": "^2.19.0", + "lru-cache": "^4.1.5", + "semver": "^5.6.0", + "sigmund": "^1.0.1" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "ejs": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz", + "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.403", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.403.tgz", + "integrity": "sha512-JaoxV4RzdBAZOnsF4dAlZ2ijJW72MbqO5lNfOBHUWiBQl3Rwe+mk2RCUMrRI3rSClLJ8HSNQNqcry12H+0ZjFw==", + "dev": true + }, + "elegant-spinner": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", + "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", + "dev": true + }, + "element-ui": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/element-ui/-/element-ui-2.13.0.tgz", + "integrity": "sha512-KYsHWsBXYbLELS8cdfvgJTOMSUby3UEjvsPV1V1VmgJ/DdkOAS4z3MiOrPxrT9w2Cc5lZ4eVSQiGhYFR5NVChw==", + "requires": { + "async-validator": "~1.8.1", + "babel-helper-vue-jsx-merge-props": "^2.0.0", + "deepmerge": "^1.2.0", + "normalize-wheel": "^1.0.1", + "resize-observer-polyfill": "^1.5.0", + "throttle-debounce": "^1.0.1" + } + }, + "elliptic": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", + "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", + "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + } + } + }, + "entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", + "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==" + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "error-stack-parser": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz", + "integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==", + "dev": true, + "requires": { + "stackframe": "^1.1.1" + } + }, + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", + "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "acorn": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", + "dev": true + }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "espree": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "inquirer": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", + "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.5.3", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "rxjs": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", + "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + }, + "strip-json-comments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", + "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + } + } + }, + "eslint-config-standard": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-12.0.0.tgz", + "integrity": "sha512-COUz8FnXhqFitYj4DTqHzidjIL/t4mumGZto5c7DrBpvWoie+Sn3P4sLEzUGeYhRElWuFEf8K1S1EfvD1vixCQ==", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz", + "integrity": "sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "eslint-loader": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-2.2.1.tgz", + "integrity": "sha512-RLgV9hoCVsMLvOxCuNjdqOrUqIj9oJg8hF44vzJaYqsAHuY9G2YAeN3joQ9nxP0p5Th9iFSIpKo+SD8KISxXRg==", + "dev": true, + "requires": { + "loader-fs-cache": "^1.0.0", + "loader-utils": "^1.0.2", + "object-assign": "^4.0.1", + "object-hash": "^1.1.4", + "rimraf": "^2.6.1" + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "eslint-plugin-cypress": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.10.3.tgz", + "integrity": "sha512-CvFeoCquShfO8gHNIKA1VpUTz78WtknMebLemBd1lRbcmJNjwpqCqpQYUG/XVja8GjdX/e2TJXYa+EUBxehtUg==", + "dev": true, + "requires": { + "globals": "^11.12.0" + } + }, + "eslint-plugin-es": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-1.4.1.tgz", + "integrity": "sha512-5fa/gR2yR3NxQf+UXkeLeP8FBBl6tSgdrAz1+cF84v1FMM4twGwQoqTnn+QxFLcPOrF4pdKEJKDB/q9GoyJrCA==", + "dev": true, + "requires": { + "eslint-utils": "^1.4.2", + "regexpp": "^2.0.1" + }, + "dependencies": { + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.20.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.2.tgz", + "integrity": "sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "array.prototype.flat": "^1.2.1", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.4.1", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.0", + "read-pkg-up": "^2.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "eslint-plugin-node": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-8.0.1.tgz", + "integrity": "sha512-ZjOjbjEi6jd82rIpFSgagv4CHWzG9xsQAVp1ZPlhRnnYxcTgENUVBvhYmkQ7GvT1QFijUSo69RaiOJKhMu6i8w==", + "dev": true, + "requires": { + "eslint-plugin-es": "^1.3.1", + "eslint-utils": "^1.3.1", + "ignore": "^5.0.2", + "minimatch": "^3.0.4", + "resolve": "^1.8.1", + "semver": "^5.5.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", + "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "dev": true + } + } + }, + "eslint-plugin-promise": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", + "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", + "dev": true + }, + "eslint-plugin-standard": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", + "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==", + "dev": true + }, + "eslint-plugin-vue": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-5.2.3.tgz", + "integrity": "sha512-mGwMqbbJf0+VvpGR5Lllq0PMxvTdrZ/ZPjmhkacrCHbubJeJOt+T6E3HUzAifa2Mxi7RSdJfC9HFpOeSYVMMIw==", + "dev": true, + "requires": { + "vue-eslint-parser": "^5.0.0" + }, + "dependencies": { + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "espree": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz", + "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==", + "dev": true, + "requires": { + "acorn": "^6.0.2", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "vue-eslint-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-5.0.0.tgz", + "integrity": "sha512-JlHVZwBBTNVvzmifwjpZYn0oPWH2SgWv5dojlZBsrhablDu95VFD+hriB1rQGwbD+bms6g+rAFhQHk6+NyiS6g==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "eslint-scope": "^4.0.0", + "eslint-visitor-keys": "^1.0.0", + "espree": "^4.1.0", + "esquery": "^1.0.1", + "lodash": "^4.17.11" + } + } + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "espree": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "dev": true, + "optional": true, + "requires": { + "acorn": "^5.5.0", + "acorn-jsx": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true, + "optional": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.2.0.tgz", + "integrity": "sha512-weltsSqdeWIX9G2qQZz7KlTRJdkkOCTPgLYJUz1Hacf48R4YOwGPHO3+ORfWedqJKbq5WQmsgK90n+pFLIKt/Q==", + "dev": true, + "requires": { + "estraverse": "^5.0.0" + }, + "dependencies": { + "estraverse": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.0.0.tgz", + "integrity": "sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "event-pubsub": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", + "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", + "dev": true + }, + "eventemitter2": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-4.1.2.tgz", + "integrity": "sha1-DhqEd6+CGm7zmVsxG/dMI6UkfxU=", + "dev": true + }, + "eventemitter3": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", + "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", + "dev": true + }, + "events": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", + "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==", + "dev": true + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "dev": true, + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "exec-sh": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.2.tgz", + "integrity": "sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw==", + "dev": true, + "requires": { + "merge": "^1.2.0" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "dev": true, + "requires": { + "pify": "^2.2.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "^2.1.0" + }, + "dependencies": { + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dev": true, + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "expect": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-23.6.0.tgz", + "integrity": "sha512-dgSoOHgmtn/aDGRVFWclQyPDKl2CQRq0hmIEoUAuQs/2rn2NcvCWcSCovm6BLeuB/7EZuLGu2QfnR+qRt5OM4w==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "jest-diff": "^23.6.0", + "jest-get-type": "^22.1.0", + "jest-matcher-utils": "^23.6.0", + "jest-message-util": "^23.4.0", + "jest-regex-util": "^23.3.0" + }, + "dependencies": { + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "jest-diff": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-23.6.0.tgz", + "integrity": "sha512-Gz9l5Ov+X3aL5L37IT+8hoCUsof1CVYBb2QEkOupK64XyRR3h+uRpYIm97K7sY8diFxowR8pIGEdyfMKTixo3g==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff": "^3.2.0", + "jest-get-type": "^22.1.0", + "pretty-format": "^23.6.0" + } + }, + "jest-get-type": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", + "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", + "dev": true + }, + "pretty-format": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz", + "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" + } + } + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "dev": true, + "optional": true, + "requires": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + }, + "dependencies": { + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "optional": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extract-from-css": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/extract-from-css/-/extract-from-css-0.4.4.tgz", + "integrity": "sha1-HqffLnx8brmSL6COitrqSG9vj5I=", + "dev": true, + "requires": { + "css": "^2.1.0" + } + }, + "extract-zip": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", + "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", + "dev": true, + "requires": { + "concat-stream": "1.6.2", + "debug": "2.6.9", + "mkdirp": "0.5.1", + "yauzl": "2.4.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "yauzl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "dev": true, + "requires": { + "fd-slicer": "~1.0.1" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, + "fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dev": true, + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "fibers": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/fibers/-/fibers-4.0.2.tgz", + "integrity": "sha512-FhICi1K4WZh9D6NC18fh2ODF3EWy1z0gzIdV9P7+s2pRjfRBnCkMDJ6x3bV1DkVymKH8HGrQa/FNOBjYvnJ/tQ==", + "dev": true, + "requires": { + "detect-libc": "^1.0.3" + } + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "dev": true + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "optional": true, + "requires": { + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" + } + }, + "file-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz", + "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==", + "dev": true, + "requires": { + "loader-utils": "^1.0.2", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fileset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", + "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", + "dev": true, + "requires": { + "glob": "^7.0.3", + "minimatch": "^3.0.3" + } + }, + "filesize": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "dev": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "find-babel-config": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-1.2.0.tgz", + "integrity": "sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA==", + "dev": true, + "requires": { + "json5": "^0.5.1", + "path-exists": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "flat-cache": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", + "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", + "dev": true, + "optional": true, + "requires": { + "circular-json": "^0.3.1", + "graceful-fs": "^4.1.2", + "rimraf": "~2.6.2", + "write": "^0.2.1" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "fork-ts-checker-webpack-plugin": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-0.5.2.tgz", + "integrity": "sha512-a5IG+xXyKnpruI0CP/anyRLAoxWtp3lzdG6flxicANnoSzz64b12dJ7ASAVRrI2OaWwZR2JyBaMHFQqInhWhIw==", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "chalk": "^2.4.1", + "chokidar": "^2.0.4", + "micromatch": "^3.1.10", + "minimatch": "^3.0.4", + "tapable": "^1.0.0" + } + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-extra": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", + "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-plus": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fs-plus/-/fs-plus-3.1.1.tgz", + "integrity": "sha512-Se2PJdOWXqos1qVTkvqqjb0CSnfBnwwD+pq+z4ksT+e97mEShod/hrNg0TRCCsXPbJzcIq+NuzQhigunMWMJUA==", + "requires": { + "async": "^1.5.2", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.2", + "underscore-plus": "1.x" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz", + "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1", + "node-pre-gyp": "*" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "3.2.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.6.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true + }, + "minipass": { + "version": "2.9.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "0.5.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.3.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.14.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4.4.2" + } + }, + "nopt": { + "version": "4.0.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.7.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.1", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.13", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "yallist": { + "version": "3.1.1", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getos": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.1.1.tgz", + "integrity": "sha512-oUP1rnEhAr97rkitiszGP9EgDVYnmchgFzfqRzSkgtfv7ai6tEi7Ko8GgjNXts7VLWEqrTWyhsOKLe5C5b/Zkg==", + "dev": true, + "requires": { + "async": "2.6.1" + }, + "dependencies": { + "async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "dev": true, + "requires": { + "lodash": "^4.17.10" + } + } + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", + "dev": true + }, + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "dev": true, + "requires": { + "ini": "^1.3.4" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globby": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", + "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^1.0.2", + "dir-glob": "^2.2.2", + "fast-glob": "^2.2.6", + "glob": "^7.1.3", + "ignore": "^4.0.3", + "pify": "^4.0.1", + "slash": "^2.0.0" + }, + "dependencies": { + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + } + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true + }, + "gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "dev": true, + "requires": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + } + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "handlebars": { + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", + "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", + "dev": true + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", + "dev": true + }, + "highlight.js": { + "version": "9.18.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.1.tgz", + "integrity": "sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg==", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoek": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", + "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==", + "dev": true + }, + "home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" + } + }, + "hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "dev": true + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", + "dev": true + }, + "hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", + "dev": true + }, + "html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", + "dev": true + }, + "html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.1" + } + }, + "html-entities": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz", + "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "dev": true, + "requires": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + }, + "dependencies": { + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + }, + "uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "dev": true, + "requires": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + } + } + } + } + }, + "html-tags": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", + "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", + "dev": true + }, + "html-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", + "dev": true, + "requires": { + "html-minifier": "^3.2.3", + "loader-utils": "^0.2.16", + "lodash": "^4.17.3", + "pretty-error": "^2.0.2", + "tapable": "^1.0.0", + "toposort": "^1.0.0", + "util.promisify": "1.0.0" + }, + "dependencies": { + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + } + } + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "http-parser-js": { + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", + "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=", + "dev": true + }, + "http-proxy": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", + "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dev": true, + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", + "dev": true + }, + "icss-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", + "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", + "dev": true, + "requires": { + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + } + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "dev": true, + "requires": { + "import-from": "^2.1.0" + } + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "dependencies": { + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + } + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + } + } + }, + "import-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", + "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", + "dev": true, + "requires": { + "pkg-dir": "^2.0.0", + "resolve-cwd": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "optional": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.0.4", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rx-lite": "^4.0.8", + "rx-lite-aggregates": "^4.0.8", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true, + "optional": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "optional": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + } + } + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "dev": true, + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + }, + "dependencies": { + "default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + } + } + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + }, + "is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "dev": true, + "requires": { + "ci-info": "^1.5.0" + } + }, + "is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "dev": true, + "requires": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "^2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "is-generator-fn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-1.0.0.tgz", + "integrity": "sha1-lp1J4bszKfa7fwkIm+JleLLd1Go=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-installed-globally": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "dev": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "requires": { + "is-path-inside": "^2.1.0" + }, + "dependencies": { + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.2" + } + } + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "requires": { + "has": "^1.0.3" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "dev": true + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, + "is-svg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", + "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", + "dev": true, + "requires": { + "html-comment-regex": "^1.1.0" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-whitespace": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz", + "integrity": "sha1-Fjnssb4DauxppUy7QBz77XEUq38=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isemail": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", + "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", + "dev": true, + "requires": { + "punycode": "2.x.x" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul-api": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.3.7.tgz", + "integrity": "sha512-4/ApBnMVeEPG3EkSzcw25wDe4N66wxwn+KKn6b47vyek8Xb3NBAcg4xfuQbS7BqcZuTX4wxfD5lVagdggR3gyA==", + "dev": true, + "requires": { + "async": "^2.1.4", + "fileset": "^2.0.2", + "istanbul-lib-coverage": "^1.2.1", + "istanbul-lib-hook": "^1.2.2", + "istanbul-lib-instrument": "^1.10.2", + "istanbul-lib-report": "^1.1.5", + "istanbul-lib-source-maps": "^1.2.6", + "istanbul-reports": "^1.5.1", + "js-yaml": "^3.7.0", + "mkdirp": "^0.5.1", + "once": "^1.4.0" + }, + "dependencies": { + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + } + } + }, + "istanbul-lib-coverage": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz", + "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.2.2.tgz", + "integrity": "sha512-/Jmq7Y1VeHnZEQ3TL10VHyb564mn6VrQXHchON9Jf/AEcmQ3ZIiyD1BVzNOKTZf/G3gE+kiGK6SmpF9y3qGPLw==", + "dev": true, + "requires": { + "append-transform": "^0.4.0" + } + }, + "istanbul-lib-instrument": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz", + "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==", + "dev": true, + "requires": { + "babel-generator": "^6.18.0", + "babel-template": "^6.16.0", + "babel-traverse": "^6.18.0", + "babel-types": "^6.18.0", + "babylon": "^6.18.0", + "istanbul-lib-coverage": "^1.2.1", + "semver": "^5.3.0" + } + }, + "istanbul-lib-report": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz", + "integrity": "sha512-UsYfRMoi6QO/doUshYNqcKJqVmFe9w51GZz8BS3WB0lYxAllQYklka2wP9+dGZeHYaWIdcXUx8JGdbqaoXRXzw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^1.2.1", + "mkdirp": "^0.5.1", + "path-parse": "^1.0.5", + "supports-color": "^3.1.2" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.6.tgz", + "integrity": "sha512-TtbsY5GIHgbMsMiRw35YBHGpZ1DVFEO19vxxeiDMYaeOFOCzfnYVxvl6pOUIZR4dtPhAGpSMup8OyF8ubsaqEg==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "istanbul-lib-coverage": "^1.2.1", + "mkdirp": "^0.5.1", + "rimraf": "^2.6.1", + "source-map": "^0.5.3" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.5.1.tgz", + "integrity": "sha512-+cfoZ0UXzWjhAdzosCPP3AN8vvef8XDkWtTfgaN+7L3YTpNYITnCaEkceo5SEYy644VkHka/P1FvkWvrG/rrJw==", + "dev": true, + "requires": { + "handlebars": "^4.0.3" + } + }, + "javascript-stringify": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-1.6.0.tgz", + "integrity": "sha1-FC0RHzpuPa6PSpr9d9RYVbWpzOM=", + "dev": true + }, + "jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", + "integrity": "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==", + "dev": true, + "requires": { + "import-local": "^2.0.0", + "jest-cli": "^24.9.0" + }, + "dependencies": { + "@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "babel-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", + "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", + "dev": true, + "requires": { + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.9.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", + "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + } + }, + "babel-plugin-jest-hoist": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", + "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", + "dev": true, + "requires": { + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", + "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", + "dev": true, + "requires": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.9.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true + }, + "expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + } + }, + "jest-cli": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", + "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==", + "dev": true, + "requires": { + "@jest/core": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "import-local": "^2.0.0", + "is-ci": "^2.0.0", + "jest-config": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^13.3.0" + } + }, + "jest-config": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.9.0", + "realpath-native": "^1.1.0" + } + }, + "jest-each": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", + "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-environment-jsdom": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", + "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0", + "jsdom": "^11.5.1" + } + }, + "jest-environment-node": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", + "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0" + } + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", + "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.9.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0", + "throat": "^4.0.0" + } + }, + "jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + }, + "jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-runtime": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", + "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^13.3.0" + } + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-snapshot": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.9.0", + "semver": "^6.2.0" + } + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + } + }, + "jest-validate": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", + "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "camelcase": "^5.3.1", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "leven": "^3.1.0", + "pretty-format": "^24.9.0" + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + } + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "prompts": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz", + "integrity": "sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.4" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "jest-changed-files": { + "version": "23.4.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-23.4.2.tgz", + "integrity": "sha512-EyNhTAUWEfwnK0Is/09LxoqNDOn7mU7S3EHskG52djOFS/z+IT0jT3h3Ql61+dklcG7bJJitIWEMB4Sp1piHmA==", + "dev": true, + "requires": { + "throat": "^4.0.0" + } + }, + "jest-config": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-23.6.0.tgz", + "integrity": "sha512-i8V7z9BeDXab1+VNo78WM0AtWpBRXJLnkT+lyT+Slx/cbP5sZJ0+NDuLcmBE5hXAoK0aUp7vI+MOxR+R4d8SRQ==", + "dev": true, + "requires": { + "babel-core": "^6.0.0", + "babel-jest": "^23.6.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^23.4.0", + "jest-environment-node": "^23.4.0", + "jest-get-type": "^22.1.0", + "jest-jasmine2": "^23.6.0", + "jest-regex-util": "^23.3.0", + "jest-resolve": "^23.6.0", + "jest-util": "^23.4.0", + "jest-validate": "^23.6.0", + "micromatch": "^2.3.11", + "pretty-format": "^23.6.0" + }, + "dependencies": { + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "jest-get-type": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", + "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "pretty-format": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz", + "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" + } + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "jest-diff": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-docblock": { + "version": "23.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-23.2.0.tgz", + "integrity": "sha1-8IXh8YVI2Z/dabICB+b9VdkTg6c=", + "dev": true, + "requires": { + "detect-newline": "^2.1.0" + } + }, + "jest-each": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-23.6.0.tgz", + "integrity": "sha512-x7V6M/WGJo6/kLoissORuvLIeAoyo2YqLOoCDkohgJ4XOXSqOtyvr8FbInlAWS77ojBsZrafbozWoKVRdtxFCg==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "pretty-format": "^23.6.0" + }, + "dependencies": { + "pretty-format": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz", + "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" + } + } + } + }, + "jest-environment-jsdom": { + "version": "23.4.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-23.4.0.tgz", + "integrity": "sha1-BWp5UrP+pROsYqFAosNox52eYCM=", + "dev": true, + "requires": { + "jest-mock": "^23.2.0", + "jest-util": "^23.4.0", + "jsdom": "^11.5.1" + } + }, + "jest-environment-node": { + "version": "23.4.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-23.4.0.tgz", + "integrity": "sha1-V+gO0IQd6jAxZ8zozXlSHeuv3hA=", + "dev": true, + "requires": { + "jest-mock": "^23.2.0", + "jest-util": "^23.4.0" + } + }, + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true + }, + "jest-haste-map": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-23.6.0.tgz", + "integrity": "sha512-uyNhMyl6dr6HaXGHp8VF7cK6KpC6G9z9LiMNsst+rJIZ8l7wY0tk8qwjPmEghczojZ2/ZhtEdIabZ0OQRJSGGg==", + "dev": true, + "requires": { + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.1.11", + "invariant": "^2.2.4", + "jest-docblock": "^23.2.0", + "jest-serializer": "^23.0.1", + "jest-worker": "^23.2.0", + "micromatch": "^2.3.11", + "sane": "^2.0.0" + }, + "dependencies": { + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "jest-jasmine2": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-23.6.0.tgz", + "integrity": "sha512-pe2Ytgs1nyCs8IvsEJRiRTPC0eVYd8L/dXJGU08GFuBwZ4sYH/lmFDdOL3ZmvJR8QKqV9MFuwlsAi/EWkFUbsQ==", + "dev": true, + "requires": { + "babel-traverse": "^6.0.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^23.6.0", + "is-generator-fn": "^1.0.0", + "jest-diff": "^23.6.0", + "jest-each": "^23.6.0", + "jest-matcher-utils": "^23.6.0", + "jest-message-util": "^23.4.0", + "jest-snapshot": "^23.6.0", + "jest-util": "^23.4.0", + "pretty-format": "^23.6.0" + }, + "dependencies": { + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "jest-diff": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-23.6.0.tgz", + "integrity": "sha512-Gz9l5Ov+X3aL5L37IT+8hoCUsof1CVYBb2QEkOupK64XyRR3h+uRpYIm97K7sY8diFxowR8pIGEdyfMKTixo3g==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff": "^3.2.0", + "jest-get-type": "^22.1.0", + "pretty-format": "^23.6.0" + } + }, + "jest-get-type": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", + "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", + "dev": true + }, + "pretty-format": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz", + "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" + } + } + } + }, + "jest-leak-detector": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-23.6.0.tgz", + "integrity": "sha512-f/8zA04rsl1Nzj10HIyEsXvYlMpMPcy0QkQilVZDFOaPbv2ur71X5u2+C4ZQJGyV/xvVXtCCZ3wQ99IgQxftCg==", + "dev": true, + "requires": { + "pretty-format": "^23.6.0" + }, + "dependencies": { + "pretty-format": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz", + "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" + } + } + } + }, + "jest-matcher-utils": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-23.6.0.tgz", + "integrity": "sha512-rosyCHQfBcol4NsckTn01cdelzWLU9Cq7aaigDf8VwwpIRvWE/9zLgX2bON+FkEW69/0UuYslUe22SOdEf2nog==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-get-type": "^22.1.0", + "pretty-format": "^23.6.0" + }, + "dependencies": { + "jest-get-type": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", + "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", + "dev": true + }, + "pretty-format": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz", + "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" + } + } + } + }, + "jest-message-util": { + "version": "23.4.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-23.4.0.tgz", + "integrity": "sha1-F2EMUJQjSVCNAaPR4L2iwHkIap8=", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0-beta.35", + "chalk": "^2.0.1", + "micromatch": "^2.3.11", + "slash": "^1.0.0", + "stack-utils": "^1.0.1" + }, + "dependencies": { + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + } + } + }, + "jest-mock": { + "version": "23.2.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-23.2.0.tgz", + "integrity": "sha1-rRxg8p6HGdR8JuETgJi20YsmETQ=", + "dev": true + }, + "jest-pnp-resolver": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", + "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", + "dev": true + }, + "jest-regex-util": { + "version": "23.3.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-23.3.0.tgz", + "integrity": "sha1-X4ZylUfCeFxAAs6qj4Sf6MpHG8U=", + "dev": true + }, + "jest-resolve": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-23.6.0.tgz", + "integrity": "sha512-XyoRxNtO7YGpQDmtQCmZjum1MljDqUCob7XlZ6jy9gsMugHdN2hY4+Acz9Qvjz2mSsOnPSH7skBmDYCHXVZqkA==", + "dev": true, + "requires": { + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "realpath-native": "^1.0.0" + } + }, + "jest-resolve-dependencies": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-23.6.0.tgz", + "integrity": "sha512-EkQWkFWjGKwRtRyIwRwI6rtPAEyPWlUC2MpzHissYnzJeHcyCn1Hc8j7Nn1xUVrS5C6W5+ZL37XTem4D4pLZdA==", + "dev": true, + "requires": { + "jest-regex-util": "^23.3.0", + "jest-snapshot": "^23.6.0" + } + }, + "jest-runner": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-23.6.0.tgz", + "integrity": "sha512-kw0+uj710dzSJKU6ygri851CObtCD9cN8aNkg8jWJf4ewFyEa6kwmiH/r/M1Ec5IL/6VFa0wnAk6w+gzUtjJzA==", + "dev": true, + "requires": { + "exit": "^0.1.2", + "graceful-fs": "^4.1.11", + "jest-config": "^23.6.0", + "jest-docblock": "^23.2.0", + "jest-haste-map": "^23.6.0", + "jest-jasmine2": "^23.6.0", + "jest-leak-detector": "^23.6.0", + "jest-message-util": "^23.4.0", + "jest-runtime": "^23.6.0", + "jest-util": "^23.4.0", + "jest-worker": "^23.2.0", + "source-map-support": "^0.5.6", + "throat": "^4.0.0" + } + }, + "jest-runtime": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-23.6.0.tgz", + "integrity": "sha512-ycnLTNPT2Gv+TRhnAYAQ0B3SryEXhhRj1kA6hBPSeZaNQkJ7GbZsxOLUkwg6YmvWGdX3BB3PYKFLDQCAE1zNOw==", + "dev": true, + "requires": { + "babel-core": "^6.0.0", + "babel-plugin-istanbul": "^4.1.6", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "exit": "^0.1.2", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.11", + "jest-config": "^23.6.0", + "jest-haste-map": "^23.6.0", + "jest-message-util": "^23.4.0", + "jest-regex-util": "^23.3.0", + "jest-resolve": "^23.6.0", + "jest-snapshot": "^23.6.0", + "jest-util": "^23.4.0", + "jest-validate": "^23.6.0", + "micromatch": "^2.3.11", + "realpath-native": "^1.0.0", + "slash": "^1.0.0", + "strip-bom": "3.0.0", + "write-file-atomic": "^2.1.0", + "yargs": "^11.0.0" + }, + "dependencies": { + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.1.tgz", + "integrity": "sha512-PRU7gJrJaXv3q3yQZ/+/X6KBswZiaQ+zOmdprZcouPYtQgvNU35i+68M4b1ZHLZtYFT5QObFLV+ZkmJYcwKdiw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" + } + }, + "yargs-parser": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", + "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "jest-serializer": { + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-23.0.1.tgz", + "integrity": "sha1-o3dq6zEekP6D+rnlM+hRAr0WQWU=", + "dev": true + }, + "jest-serializer-vue": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jest-serializer-vue/-/jest-serializer-vue-2.0.2.tgz", + "integrity": "sha1-sjjvKGNX7GtIBCG9RxRQUJh9WbM=", + "dev": true, + "requires": { + "pretty": "2.0.0" + } + }, + "jest-snapshot": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-23.6.0.tgz", + "integrity": "sha512-tM7/Bprftun6Cvj2Awh/ikS7zV3pVwjRYU2qNYS51VZHgaAMBs5l4o/69AiDHhQrj5+LA2Lq4VIvK7zYk/bswg==", + "dev": true, + "requires": { + "babel-types": "^6.0.0", + "chalk": "^2.0.1", + "jest-diff": "^23.6.0", + "jest-matcher-utils": "^23.6.0", + "jest-message-util": "^23.4.0", + "jest-resolve": "^23.6.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^23.6.0", + "semver": "^5.5.0" + }, + "dependencies": { + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "jest-diff": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-23.6.0.tgz", + "integrity": "sha512-Gz9l5Ov+X3aL5L37IT+8hoCUsof1CVYBb2QEkOupK64XyRR3h+uRpYIm97K7sY8diFxowR8pIGEdyfMKTixo3g==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff": "^3.2.0", + "jest-get-type": "^22.1.0", + "pretty-format": "^23.6.0" + } + }, + "jest-get-type": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", + "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", + "dev": true + }, + "pretty-format": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz", + "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" + } + } + } + }, + "jest-transform-stub": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jest-transform-stub/-/jest-transform-stub-2.0.0.tgz", + "integrity": "sha512-lspHaCRx/mBbnm3h4uMMS3R5aZzMwyNpNIJLXj4cEsV0mIUtS4IjYJLSoyjRCtnxb6RIGJ4NL2quZzfIeNhbkg==", + "dev": true + }, + "jest-util": { + "version": "23.4.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-23.4.0.tgz", + "integrity": "sha1-TQY8uSe68KI4Mf9hvsLLv0l5NWE=", + "dev": true, + "requires": { + "callsites": "^2.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.11", + "is-ci": "^1.0.10", + "jest-message-util": "^23.4.0", + "mkdirp": "^0.5.1", + "slash": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + } + } + }, + "jest-validate": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-23.6.0.tgz", + "integrity": "sha512-OFKapYxe72yz7agrDAWi8v2WL8GIfVqcbKRCLbRG9PAxtzF9b1SEDdTpytNDN12z2fJynoBwpMpvj2R39plI2A==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-get-type": "^22.1.0", + "leven": "^2.1.0", + "pretty-format": "^23.6.0" + }, + "dependencies": { + "jest-get-type": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", + "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", + "dev": true + }, + "pretty-format": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz", + "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" + } + } + } + }, + "jest-watch-typeahead": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-0.2.1.tgz", + "integrity": "sha512-xdhEtKSj0gmnkDQbPTIHvcMmXNUDzYpHLEJ5TFqlaI+schi2NI96xhWiZk9QoesAS7oBmKwWWsHazTrYl2ORgg==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.4.1", + "jest-watcher": "^23.1.0", + "slash": "^2.0.0", + "string-length": "^2.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "jest-watcher": { + "version": "23.4.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-23.4.0.tgz", + "integrity": "sha1-0uKM50+NrWxq/JIrksq+9u0FyRw=", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "string-length": "^2.0.0" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + } + } + }, + "jest-worker": { + "version": "23.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-23.2.0.tgz", + "integrity": "sha1-+vcGqNo2+uYOsmlXJX+ntdjqArk=", + "dev": true, + "requires": { + "merge-stream": "^1.0.1" + } + }, + "joi": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-11.4.0.tgz", + "integrity": "sha512-O7Uw+w/zEWgbL6OcHbyACKSj0PkQeUgmehdoXVSxt92QFCq4+1390Rwh5moI2K/OgC7D8RHRZqHZxT2husMJHA==", + "dev": true, + "requires": { + "hoek": "4.x.x", + "isemail": "3.x.x", + "topo": "2.x.x" + } + }, + "js-beautify": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.11.0.tgz", + "integrity": "sha512-a26B+Cx7USQGSWnz9YxgJNMmML/QG2nqIaL7VVYPCXbqiKz8PN0waSNvroMtvAK6tY7g/wPdNWGEP+JTNIBr6A==", + "dev": true, + "requires": { + "config-chain": "^1.1.12", + "editorconfig": "^0.15.3", + "glob": "^7.1.3", + "mkdirp": "~1.0.3", + "nopt": "^4.0.3" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + } + } + }, + "js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" + }, + "js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true + }, + "js-message": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", + "integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=", + "dev": true + }, + "js-queue": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz", + "integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=", + "dev": true, + "requires": { + "easy-stack": "^1.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsdom": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", + "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "acorn": "^5.5.3", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": "^1.0.0", + "data-urls": "^1.0.0", + "domexception": "^1.0.1", + "escodegen": "^1.9.1", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.3.0", + "nwsapi": "^2.0.7", + "parse5": "4.0.0", + "pn": "^1.1.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.4", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^6.4.1", + "ws": "^5.2.0", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "~0.0.0" + } + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", + "dev": true + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "kleur": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-2.0.2.tgz", + "integrity": "sha512-77XF9iTllATmG9lSlIv0qdQ2BQ/h9t0bJllHlbvsQ0zUWfU7Yi0S8L5JXzPZgkefIiajLmBJJ4BsMJmqcf7oxQ==", + "dev": true + }, + "launch-editor": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.2.1.tgz", + "integrity": "sha512-On+V7K2uZK6wK7x691ycSUbLD/FyKKelArkbaAMSSJU8JmqmhwN2+mnJDNINuJWSrh2L0kDk+ZQtbC/gOWUwLw==", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "shell-quote": "^1.6.1" + } + }, + "launch-editor-middleware": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/launch-editor-middleware/-/launch-editor-middleware-2.2.1.tgz", + "integrity": "sha512-s0UO2/gEGiCgei3/2UN3SMuUj1phjQN8lcpnvgLSz26fAzNWPQ6Nf/kF5IFClnfU2ehp6LrmKdMU/beveO+2jg==", + "dev": true, + "requires": { + "launch-editor": "^2.2.1" + } + }, + "lazy-ass": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", + "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "requires": { + "invert-kv": "^2.0.0" + } + }, + "left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", + "dev": true + }, + "leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "listr": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/listr/-/listr-0.12.0.tgz", + "integrity": "sha1-a84sD1YD+klYDqF81qAMwOX6RRo=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-truncate": "^0.2.1", + "figures": "^1.7.0", + "indent-string": "^2.1.0", + "is-promise": "^2.1.0", + "is-stream": "^1.1.0", + "listr-silent-renderer": "^1.1.1", + "listr-update-renderer": "^0.2.0", + "listr-verbose-renderer": "^0.4.0", + "log-symbols": "^1.0.2", + "log-update": "^1.0.2", + "ora": "^0.2.3", + "p-map": "^1.1.1", + "rxjs": "^5.0.0-beta.11", + "stream-to-observable": "^0.1.0", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "cli-spinners": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz", + "integrity": "sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw=", + "dev": true + }, + "log-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", + "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "dev": true, + "requires": { + "chalk": "^1.0.0" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "ora": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/ora/-/ora-0.2.3.tgz", + "integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=", + "dev": true, + "requires": { + "chalk": "^1.1.1", + "cli-cursor": "^1.0.2", + "cli-spinners": "^0.1.2", + "object-assign": "^4.0.1" + } + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "listr-silent-renderer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", + "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", + "dev": true + }, + "listr-update-renderer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz", + "integrity": "sha1-yoDhd5tOcCZoB+ju0a1qvjmFUPk=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-truncate": "^0.2.1", + "elegant-spinner": "^1.0.1", + "figures": "^1.7.0", + "indent-string": "^3.0.0", + "log-symbols": "^1.0.2", + "log-update": "^1.0.2", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "log-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", + "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "dev": true, + "requires": { + "chalk": "^1.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "listr-verbose-renderer": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", + "integrity": "sha1-ggb0z21S3cWCfl/RSYng6WWTOjU=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-cursor": "^1.0.2", + "date-fns": "^1.27.2", + "figures": "^1.7.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "loader-fs-cache": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz", + "integrity": "sha512-ldcgZpjNJj71n+2Mf6yetz+c9bM4xpKtNds4LbqXzU/PTdeAX0g3ytnU1AJMEcTk2Lex4Smpe3Q/eCTsvUBxbA==", + "dev": true, + "requires": { + "find-cache-dir": "^0.1.1", + "mkdirp": "^0.5.1" + }, + "dependencies": { + "find-cache-dir": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", + "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "mkdirp": "^0.5.1", + "pkg-dir": "^1.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "dev": true, + "requires": { + "find-up": "^1.0.0" + } + } + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash.defaultsdeep": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz", + "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==", + "dev": true + }, + "lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=", + "dev": true + }, + "lodash.mapvalues": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", + "integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=", + "dev": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", + "dev": true + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, + "lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "lodash.transform": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.transform/-/lodash.transform-4.6.0.tgz", + "integrity": "sha1-EjBkIvYzJK7YSD0/ODMrX2cFR6A=", + "dev": true + }, + "lodash.unescape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", + "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "log-update": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz", + "integrity": "sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE=", + "dev": true, + "requires": { + "ansi-escapes": "^1.0.0", + "cli-cursor": "^1.0.2" + }, + "dependencies": { + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + } + } + }, + "loglevel": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.7.tgz", + "integrity": "sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "dev": true + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "merge": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", + "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", + "dev": true + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + } + }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, + "merge2": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", + "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", + "dev": true + }, + "mime-db": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", + "dev": true + }, + "mime-types": { + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", + "dev": true, + "requires": { + "mime-db": "1.43.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "mini-css-extract-plugin": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.2.tgz", + "integrity": "sha512-a3Y4of27Wz+mqK3qrcd3VhYz6cU0iW5x3Sgvqzbj+XmlrSizmvu8QQMl5oMYJjgHOC4iyt+w7l4umP+dQeW3bw==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "normalize-url": "1.9.1", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", + "dev": true + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true, + "optional": true + }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-cache": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-4.2.1.tgz", + "integrity": "sha512-BOb67bWg2dTyax5kdef5WfU3X8xu4wPg+zHzkvls0Q/QpYycIFRLEEIdAx9Wma43DxG6Qzn4illdZoYseKWa4A==", + "dev": true, + "requires": { + "clone": "2.x", + "lodash": "^4.17.15" + }, + "dependencies": { + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + } + } + }, + "node-forge": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", + "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==", + "dev": true + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-ipc": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.1.tgz", + "integrity": "sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w==", + "dev": true, + "requires": { + "event-pubsub": "4.3.0", + "js-message": "1.0.5", + "js-queue": "2.0.0" + } + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true + }, + "node-notifier": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz", + "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==", + "dev": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", + "shellwords": "^0.1.1", + "which": "^1.3.0" + } + }, + "node-releases": { + "version": "1.1.53", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.53.tgz", + "integrity": "sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ==", + "dev": true + }, + "nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "dev": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "dev": true + }, + "normalize-wheel": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/normalize-wheel/-/normalize-wheel-1.0.1.tgz", + "integrity": "sha1-rsiGr/2wRQcNhWRH32Ls+GFG7EU=" + }, + "normalize.css": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", + "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha1-y480xTIT2JVyP8urkH6UIq28r7E=" + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-hash": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", + "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==", + "dev": true + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + }, + "object-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz", + "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + }, + "dependencies": { + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + } + } + }, + "open": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "opener": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", + "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", + "dev": true + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "ora": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", + "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-spinners": "^2.0.0", + "log-symbols": "^2.2.0", + "strip-ansi": "^5.2.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dev": true, + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" + }, + "p-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", + "dev": true, + "requires": { + "p-reduce": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==" + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", + "dev": true + }, + "p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dev": true, + "requires": { + "retry": "^0.12.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dev": true, + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "requires": { + "no-case": "^2.2.0" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + } + } + }, + "parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + }, + "parse5-htmlparser2-tree-adapter": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-5.1.1.tgz", + "integrity": "sha512-CF+TKjXqoqyDwHqBhFQ+3l5t83xYi6fVT1tQNg+Ye0JRLnTxWvIroCjEp1A0k4lneHNBGnICUf0cfYVYGEazqw==", + "dev": true, + "requires": { + "parse5": "^5.1.1" + }, + "dependencies": { + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true + } + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-regexp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz", + "integrity": "sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==" + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + } + } + }, + "pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true, + "optional": true + }, + "pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", + "dev": true + }, + "portfinder": { + "version": "1.0.25", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", + "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.1" + }, + "dependencies": { + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", + "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-calc": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.2.tgz", + "integrity": "sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ==", + "dev": true, + "requires": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-load-config": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", + "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + } + }, + "postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "dev": true, + "requires": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-modules-extract-imports": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz", + "integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==", + "dev": true, + "requires": { + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + } + } + }, + "postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + } + } + }, + "postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + } + } + }, + "postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "dev": true, + "requires": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + } + } + }, + "postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "dev": true, + "requires": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "dev": true, + "requires": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-selector-parser": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", + "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postcss-svgo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", + "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", + "dev": true, + "requires": { + "is-svg": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + } + }, + "postcss-value-parser": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz", + "integrity": "sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true, + "optional": true + }, + "pretty": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pretty/-/pretty-2.0.0.tgz", + "integrity": "sha1-rbx5YLe7/iiaVX3F9zdhmiINBqU=", + "dev": true, + "requires": { + "condense-newlines": "^0.2.1", + "extend-shallow": "^2.0.1", + "js-beautify": "^1.6.12" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "pretty-bytes": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz", + "integrity": "sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=", + "dev": true + }, + "pretty-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", + "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", + "dev": true, + "requires": { + "renderkid": "^2.0.1", + "utila": "~0.4" + } + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "prompts": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-0.1.14.tgz", + "integrity": "sha512-rxkyiE9YH6zAz/rZpywySLKkpaj0NMVyNw1qhsubdbjjSgcayjTShDreZGlFMcGSu5sab3bAKPfFk78PB90+8w==", + "dev": true, + "requires": { + "kleur": "^2.0.1", + "sisteransi": "^0.1.1" + } + }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "dev": true + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", + "dev": true + }, + "ramda": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz", + "integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc=", + "dev": true + }, + "randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "dev": true, + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "dependencies": { + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + } + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "realpath-native": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", + "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "dev": true, + "requires": { + "util.promisify": "^1.0.0" + } + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "dev": true, + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "regenerator-transform": { + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.4.tgz", + "integrity": "sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4", + "private": "^0.1.8" + } + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "^0.1.3" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "regexpp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", + "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", + "dev": true, + "optional": true + }, + "regexpu-core": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", + "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", + "dev": true, + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" + } + }, + "register-service-worker": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/register-service-worker/-/register-service-worker-1.7.1.tgz", + "integrity": "sha512-IdTfUZ4u8iJL8o1w8es8l6UMGPmkwHolUdT+UmM1UypC80IB4KbpuIlvwWVj8UDS7eJwkEYRcKRgfRX+oTmJsw==" + }, + "regjsgen": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", + "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", + "dev": true + }, + "regjsparser": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", + "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "renderkid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz", + "integrity": "sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==", + "dev": true, + "requires": { + "css-select": "^1.1.0", + "dom-converter": "^0.2", + "htmlparser2": "^3.3.0", + "strip-ansi": "^3.0.0", + "utila": "^0.4.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "dev": true + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "request-progress": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", + "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", + "dev": true, + "requires": { + "throttleit": "^1.0.0" + } + }, + "request-promise-core": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", + "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, + "request-promise-native": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", + "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", + "dev": true, + "requires": { + "request-promise-core": "1.1.3", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "optional": true, + "requires": { + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" + } + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "reselect": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-3.0.1.tgz", + "integrity": "sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc=", + "dev": true + }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + } + } + }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true, + "optional": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, + "rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", + "dev": true + }, + "rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rsvp": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz", + "integrity": "sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw==", + "dev": true + }, + "run-async": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", + "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "dev": true, + "optional": true + }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "optional": true, + "requires": { + "rx-lite": "*" + } + }, + "rxjs": { + "version": "5.5.12", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", + "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", + "dev": true, + "requires": { + "symbol-observable": "1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sane": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/sane/-/sane-2.5.2.tgz", + "integrity": "sha1-tNwYYcIbQn6SlQej51HiosuKs/o=", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "capture-exit": "^1.2.0", + "exec-sh": "^0.2.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.3", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5", + "watch": "~0.18.0" + } + }, + "sass": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.3.tgz", + "integrity": "sha512-5NMHI1+YFYw4sN3yfKjpLuV9B5l7MqQ6FlkTcC4FT+oHbBRUZoSjHrrt/mE0nFXJyY2kQtU9ou9HxvFVjLFuuw==", + "dev": true, + "requires": { + "chokidar": ">=2.0.0 <4.0.0" + } + }, + "sass-loader": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.3.1.tgz", + "integrity": "sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "loader-utils": "^1.0.1", + "neo-async": "^2.5.0", + "pify": "^4.0.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "schema-utils": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz", + "integrity": "sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==", + "dev": true, + "requires": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selfsigned": { + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", + "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==", + "dev": true, + "requires": { + "node-forge": "0.9.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", + "dev": true + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", + "dev": true + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + } + } + }, + "sisteransi": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-0.1.1.tgz", + "integrity": "sha512-PmGOd02bM9YO5ifxpw36nrNMBTptEtfRl4qUYl9SndkolplkrZZOW7PGHjrZL53QvMVj9nQ+TKqUnRsw4tJa4g==", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "sockjs": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "dev": true, + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.0.1" + } + }, + "sockjs-client": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", + "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", + "dev": true, + "requires": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "faye-websocket": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", + "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", + "dev": true + }, + "stackframe": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.1.1.tgz", + "integrity": "sha512-0PlYhdKh6AfFxRyK/v+6/k+/mMfyiEBbTM5L94D0ZytQnJ166wuwoTYLHFWGbs2dpA8Rgq763KGWmN1EQEYHRQ==", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true + }, + "stream-to-observable": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stream-to-observable/-/stream-to-observable-0.1.0.tgz", + "integrity": "sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4=", + "dev": true + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, + "string-length": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", + "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "dev": true, + "requires": { + "astral-regex": "^1.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string.prototype.padend": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.0.tgz", + "integrity": "sha512-3aIv8Ffdp8EZj8iLwREGpQaUZiPyrWrpzMBHvkiSW/bK/EGve9np07Vwy7IJ5waydpGXzQZu/F8Oze2/IWkBaA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "string.prototype.padstart": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.padstart/-/string.prototype.padstart-3.1.0.tgz", + "integrity": "sha512-envqZvUp2JItI+OeQ5UAh1ihbAV5G/2bixTojvlIa090GGqF+NQRxbWb2nv9fTGrZABv6+pE6jXoAZhhS2k4Hw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trimleft": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", + "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimstart": "^1.0.0" + } + }, + "string.prototype.trimright": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", + "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimend": "^1.0.0" + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-comments": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-1.0.2.tgz", + "integrity": "sha512-kL97alc47hoyIQSV165tTt9rG5dn4w1dNnBhOQ3bOU1Nc1hel09jnXANaHJ7vzHLd4Ju8kseDGzlev96pghLFw==", + "dev": true, + "requires": { + "babel-extract-comments": "^1.0.0", + "babel-plugin-transform-object-rest-spread": "^6.26.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "style-resources-loader": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/style-resources-loader/-/style-resources-loader-1.3.3.tgz", + "integrity": "sha512-vDD2HyG6On8H9gWUN9O9q1eXR/JnXpCkNvpusvgFsRQ9JZGF9drzvwKEigR9vqlmUbXO2t/vIIabpYMmis0eAQ==", + "dev": true, + "requires": { + "glob": "^7.1.6", + "is-promise": "^2.1.0", + "loader-utils": "^1.2.3", + "schema-utils": "^2.6.1" + } + }, + "stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", + "dev": true + }, + "svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + } + }, + "symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", + "dev": true + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "table": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", + "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "dev": true, + "optional": true, + "requires": { + "ajv": "^5.2.3", + "ajv-keywords": "^2.1.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", + "slice-ansi": "1.0.0", + "string-width": "^2.1.1" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "optional": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "ajv-keywords": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", + "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "dev": true, + "optional": true + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true, + "optional": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true, + "optional": true + }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "optional": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0" + } + } + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "terser": { + "version": "4.6.11", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.11.tgz", + "integrity": "sha512-76Ynm7OXUG5xhOpblhytE7X58oeNSmC8xnNhjWVo8CksHit0U0kO4hfNbPrrYwowLWFgM2n9L176VNx2QaHmtA==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + } + }, + "terser-webpack-plugin": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", + "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", + "dev": true, + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^2.1.2", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "test-exclude": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.2.3.tgz", + "integrity": "sha512-SYbXgY64PT+4GAL2ocI3HwPa4Q4TBKm0cwAVeKOt/Aoc0gSpNRjJX8w0pA1LMKZ3LBmd8pYBqApFNQLII9kavA==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "micromatch": "^2.3.11", + "object-assign": "^4.1.0", + "read-pkg-up": "^1.0.1", + "require-main-filename": "^1.0.1" + }, + "dependencies": { + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "thenify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", + "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "dev": true, + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "dev": true, + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, + "thread-loader": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/thread-loader/-/thread-loader-2.1.3.tgz", + "integrity": "sha512-wNrVKH2Lcf8ZrWxDF/khdlLlsTMczdcwPA9VEK4c2exlEPynYWxi9op3nPTo5lAnDIkE0rQEB3VBP+4Zncc9Hg==", + "dev": true, + "requires": { + "loader-runner": "^2.3.1", + "loader-utils": "^1.1.0", + "neo-async": "^2.6.0" + } + }, + "throat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", + "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", + "dev": true + }, + "throttle-debounce": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-1.1.0.tgz", + "integrity": "sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg==" + }, + "throttleit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", + "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", + "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", + "dev": true + }, + "tmp": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", + "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", + "dev": true, + "requires": { + "rimraf": "^2.6.3" + } + }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, + "topo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/topo/-/topo-2.0.2.tgz", + "integrity": "sha1-zVYVdSU5BXwNwEkaYhw7xvvh0YI=", + "dev": true, + "requires": { + "hoek": "4.x.x" + } + }, + "toposort": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", + "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", + "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", + "dev": true + }, + "ts-jest": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-24.3.0.tgz", + "integrity": "sha512-Hb94C/+QRIgjVZlJyiWwouYUF+siNJHJHknyspaOcZ+OQAIdFG/UrdQVXw/0B8Z3No34xkUXZJpOTy9alOWdVQ==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "mkdirp": "0.x", + "resolve": "1.x", + "semver": "^5.5", + "yargs-parser": "10.x" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "ts-loader": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.4.5.tgz", + "integrity": "sha512-XYsjfnRQCBum9AMRZpk2rTYSVpdZBpZK+kDh0TeT3kxmQNBDVIeUjdPjY5RZry4eIAb8XHc4gYSUiUWPYvzSRw==", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^1.0.2", + "micromatch": "^3.1.4", + "semver": "^5.0.1" + } + }, + "tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "dev": true, + "requires": { + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "tslib": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==" + }, + "tslint": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz", + "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.29.0" + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.2.tgz", + "integrity": "sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw==", + "dev": true + }, + "uglify-js": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.1.tgz", + "integrity": "sha512-W7KxyzeaQmZvUFbGj4+YFshhVrMBGSg2IbcYAjGWGvx8DHvJMclbTDMpffdxFUGPBHjIytk7KJUR/KUXstUGDw==", + "dev": true, + "optional": true, + "requires": { + "commander": "~2.20.3", + "source-map": "~0.6.1" + } + }, + "underscore": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", + "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==" + }, + "underscore-plus": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore-plus/-/underscore-plus-1.7.0.tgz", + "integrity": "sha512-A3BEzkeicFLnr+U/Q3EyWwJAQPbA19mtZZ4h+lLq3ttm9kn8WC4R3YpuJZEXmWdLjYP47Zc8aLZm9kwdv+zzvA==", + "requires": { + "underscore": "^1.9.1" + } + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "untildify": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.3.tgz", + "integrity": "sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA==", + "dev": true + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-loader": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz", + "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "mime": "^2.0.3", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "vue": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz", + "integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==" + }, + "vue-class-component": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-7.2.3.tgz", + "integrity": "sha512-oEqYpXKaFN+TaXU+mRLEx8dX0ah85aAJEe61mpdoUrq0Bhe/6sWhyZX1JjMQLhVsHAkncyhedhmCdDVSasUtDw==" + }, + "vue-cli-plugin-element": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vue-cli-plugin-element/-/vue-cli-plugin-element-1.0.1.tgz", + "integrity": "sha512-OJSOnJtn7f1v/8xX+MJae+RrE8WguhiiG9QTBx/MNOPXYsxqut6Ommo+ZD3raNc7eryhqdM2T/DlMfdvIKpCtw==", + "dev": true + }, + "vue-cli-plugin-style-resources-loader": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/vue-cli-plugin-style-resources-loader/-/vue-cli-plugin-style-resources-loader-0.1.4.tgz", + "integrity": "sha512-aK2TyVThZO6oVPZJpCckha8ALcg1RXKYLNqtuy1fNv+24x6zGWKpSK+FHAo47B7yeRlFS9DbKo+cvUFMqWaZ7Q==", + "dev": true + }, + "vue-eslint-parser": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz", + "integrity": "sha512-ZezcU71Owm84xVF6gfurBQUGg8WQ+WZGxgDEQu1IHFBZNx7BFZg3L1yHxrCBNNwbwFtE1GuvfJKMtb6Xuwc/Bw==", + "dev": true, + "optional": true, + "requires": { + "debug": "^3.1.0", + "eslint-scope": "^3.7.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^3.5.2", + "esquery": "^1.0.0", + "lodash": "^4.17.4" + }, + "dependencies": { + "eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", + "dev": true, + "optional": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + } + } + }, + "vue-hot-reload-api": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", + "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", + "dev": true + }, + "vue-jest": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/vue-jest/-/vue-jest-3.0.5.tgz", + "integrity": "sha512-xWDxde91pDqYBGDlODENZ3ezPgw+IQFoVDtf+5Awlg466w3KvMSqWzs8PxcTeTr+wmAHi0j+a+Lm3R7aUJa1jA==", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", + "chalk": "^2.1.0", + "extract-from-css": "^0.4.4", + "find-babel-config": "^1.1.0", + "js-beautify": "^1.6.14", + "node-cache": "^4.1.1", + "object-assign": "^4.1.1", + "source-map": "^0.5.6", + "tsconfig": "^7.0.0", + "vue-template-es2015-compiler": "^1.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "vue-loader": { + "version": "15.9.1", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.1.tgz", + "integrity": "sha512-IaPU2KOPjs/QjMlxFs/TiTtQUSbftQ7lsAvoxe21rtcQohsMhx+1AltXCNhZIpIn46PtODiAgz+o8RbMpKtmJw==", + "dev": true, + "requires": { + "@vue/component-compiler-utils": "^3.1.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "vue-hot-reload-api": "^2.3.0", + "vue-style-loader": "^4.1.0" + } + }, + "vue-property-decorator": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/vue-property-decorator/-/vue-property-decorator-8.4.1.tgz", + "integrity": "sha512-8R4Us7DxFO0rwAL/2fv6vaZL8Oa4n/HGanHODYGTcvQHwT0FYJr9UuuFm2GoWAIXQu1mDO03HNeHswFp9vmTVA==", + "requires": { + "vue-class-component": "^7.1.0" + } + }, + "vue-router": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.1.6.tgz", + "integrity": "sha512-GYhn2ynaZlysZMkFE5oCHRUTqE8BWs/a9YbKpNLi0i7xD6KG1EzDqpHQmv1F5gXjr8kL5iIVS8EOtRaVUEXTqA==" + }, + "vue-style-loader": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz", + "integrity": "sha512-0ip8ge6Gzz/Bk0iHovU9XAUQaFt/G2B61bnWa2tCcqqdgfHs1lF9xXorFbE55Gmy92okFT+8bfmySuUOu13vxQ==", + "dev": true, + "requires": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + } + }, + "vue-svgicon": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/vue-svgicon/-/vue-svgicon-3.2.6.tgz", + "integrity": "sha512-ZmX+s4H3lJkJLL8+Fg5a8N0tLQow1B5cVYo4yBr5777gS6uFSw6/ZXBITmtlb+nhcCgWhULA43hMTH5b9goIHA==", + "requires": { + "camelcase": "^5.2.0", + "colors": "^1.3.0", + "fs-plus": "^3.0.2", + "glob": "^7.1.2", + "svgo": "^1.0.5", + "tslib": "^1.9.3", + "yargs": "^12.0.1" + } + }, + "vue-template-compiler": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz", + "integrity": "sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==", + "dev": true, + "requires": { + "de-indent": "^1.0.2", + "he": "^1.1.0" + } + }, + "vue-template-es2015-compiler": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", + "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", + "dev": true + }, + "vuex": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.1.3.tgz", + "integrity": "sha512-k8vZqNMSNMgKelVZAPYw5MNb2xWSmVgCKtYKAptvm9YtZiOXnRXFWu//Y9zQNORTrm3dNj1n/WaZZI26tIX6Mw==" + }, + "vuex-class": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/vuex-class/-/vuex-class-0.3.2.tgz", + "integrity": "sha512-m0w7/FMsNcwJgunJeM+wcNaHzK2KX1K1rw2WUQf7Q16ndXHo7pflRyOV/E8795JO/7fstyjH3EgqBI4h4n4qXQ==" + }, + "vuex-module-decorators": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/vuex-module-decorators/-/vuex-module-decorators-0.10.1.tgz", + "integrity": "sha512-OBvHEeer8uT///oTWqgf7kPe0j3hb9IC9ahdk41S4gBHhn5YUHpyXdry2OXp77zPzQ1P6wTY+Ju+/VUcU5mhzA==" + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } + }, + "watch": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/watch/-/watch-0.18.0.tgz", + "integrity": "sha1-KAlUdsbffJDJYxOJkMClQj60uYY=", + "dev": true, + "requires": { + "exec-sh": "^0.2.0", + "minimist": "^1.2.0" + } + }, + "watchpack": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz", + "integrity": "sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA==", + "dev": true, + "requires": { + "chokidar": "^2.1.8", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true + }, + "webpack": { + "version": "4.42.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.1.tgz", + "integrity": "sha512-SGfYMigqEfdGchGhFFJ9KyRpQKnipvEvjc1TwrXEPCM6H5Wywu10ka8o3KGrMzSMxMQKt8aCHUFh5DaQ9UmyRg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.2.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.6.0", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "webpack-bundle-analyzer": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.6.1.tgz", + "integrity": "sha512-Nfd8HDwfSx1xBwC+P8QMGvHAOITxNBSvu/J/mCJvOwv+G4VWkU7zir9SSenTtyCi0LnVtmsc7G5SZo1uV+bxRw==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1", + "bfj": "^6.1.1", + "chalk": "^2.4.1", + "commander": "^2.18.0", + "ejs": "^2.6.1", + "express": "^4.16.3", + "filesize": "^3.6.1", + "gzip-size": "^5.0.0", + "lodash": "^4.17.15", + "mkdirp": "^0.5.1", + "opener": "^1.5.1", + "ws": "^6.0.0" + }, + "dependencies": { + "acorn": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", + "dev": true + }, + "acorn-walk": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz", + "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==", + "dev": true + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "webpack-chain": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-4.12.1.tgz", + "integrity": "sha512-BCfKo2YkDe2ByqkEWe1Rw+zko4LsyS75LVr29C6xIrxAg9JHJ4pl8kaIZ396SUSNp6b4815dRZPSTAS8LlURRQ==", + "dev": true, + "requires": { + "deepmerge": "^1.5.2", + "javascript-stringify": "^1.6.0" + } + }, + "webpack-dev-middleware": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", + "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", + "dev": true, + "requires": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + } + }, + "webpack-dev-server": { + "version": "3.10.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.10.3.tgz", + "integrity": "sha512-e4nWev8YzEVNdOMcNzNeCN947sWJNd43E5XvsJzbAL08kGc2frm1tQ32hTJslRS+H65LCb/AaUCYU7fjHCpDeQ==", + "dev": true, + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.2.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.6", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.25", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.7", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "0.3.19", + "sockjs-client": "1.4.0", + "spdy": "^4.0.1", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "12.0.5" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + } + }, + "webpack-merge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", + "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "websocket-driver": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", + "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.4.0 <0.4.11", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "dev": true + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "workbox-background-sync": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-3.6.3.tgz", + "integrity": "sha512-ypLo0B6dces4gSpaslmDg5wuoUWrHHVJfFWwl1udvSylLdXvnrfhFfriCS42SNEe5lsZtcNZF27W/SMzBlva7Q==", + "dev": true, + "requires": { + "workbox-core": "^3.6.3" + } + }, + "workbox-broadcast-cache-update": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/workbox-broadcast-cache-update/-/workbox-broadcast-cache-update-3.6.3.tgz", + "integrity": "sha512-pJl4lbClQcvp0SyTiEw0zLSsVYE1RDlCPtpKnpMjxFtu8lCFTAEuVyzxp9w7GF4/b3P4h5nyQ+q7V9mIR7YzGg==", + "dev": true, + "requires": { + "workbox-core": "^3.6.3" + } + }, + "workbox-build": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-3.6.3.tgz", + "integrity": "sha512-w0clZ/pVjL8VXy6GfthefxpEXs0T8uiRuopZSFVQ8ovfbH6c6kUpEh6DcYwm/Y6dyWPiCucdyAZotgjz+nRz8g==", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "common-tags": "^1.4.0", + "fs-extra": "^4.0.2", + "glob": "^7.1.2", + "joi": "^11.1.1", + "lodash.template": "^4.4.0", + "pretty-bytes": "^4.0.2", + "stringify-object": "^3.2.2", + "strip-comments": "^1.0.2", + "workbox-background-sync": "^3.6.3", + "workbox-broadcast-cache-update": "^3.6.3", + "workbox-cache-expiration": "^3.6.3", + "workbox-cacheable-response": "^3.6.3", + "workbox-core": "^3.6.3", + "workbox-google-analytics": "^3.6.3", + "workbox-navigation-preload": "^3.6.3", + "workbox-precaching": "^3.6.3", + "workbox-range-requests": "^3.6.3", + "workbox-routing": "^3.6.3", + "workbox-strategies": "^3.6.3", + "workbox-streams": "^3.6.3", + "workbox-sw": "^3.6.3" + }, + "dependencies": { + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, + "workbox-cache-expiration": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/workbox-cache-expiration/-/workbox-cache-expiration-3.6.3.tgz", + "integrity": "sha512-+ECNph/6doYx89oopO/UolYdDmQtGUgo8KCgluwBF/RieyA1ZOFKfrSiNjztxOrGJoyBB7raTIOlEEwZ1LaHoA==", + "dev": true, + "requires": { + "workbox-core": "^3.6.3" + } + }, + "workbox-cacheable-response": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-3.6.3.tgz", + "integrity": "sha512-QpmbGA9SLcA7fklBLm06C4zFg577Dt8u3QgLM0eMnnbaVv3rhm4vbmDpBkyTqvgK/Ly8MBDQzlXDtUCswQwqqg==", + "dev": true, + "requires": { + "workbox-core": "^3.6.3" + } + }, + "workbox-core": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-3.6.3.tgz", + "integrity": "sha512-cx9cx0nscPkIWs8Pt98HGrS9/aORuUcSkWjG25GqNWdvD/pSe7/5Oh3BKs0fC+rUshCiyLbxW54q0hA+GqZeSQ==", + "dev": true + }, + "workbox-google-analytics": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-3.6.3.tgz", + "integrity": "sha512-RQBUo/6SXtIaQTRFj4RQZ9e1gAl7D8oS5S+Hi173Kk70/BgJjzPwXpC5A249Jv5YfkCOLMQCeF9A27BiD0b0ig==", + "dev": true, + "requires": { + "workbox-background-sync": "^3.6.3", + "workbox-core": "^3.6.3", + "workbox-routing": "^3.6.3", + "workbox-strategies": "^3.6.3" + } + }, + "workbox-navigation-preload": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-3.6.3.tgz", + "integrity": "sha512-dd26xTX16DUu0i+MhqZK/jQXgfIitu0yATM4jhRXEmpMqQ4MxEeNvl2CgjDMOHBnCVMax+CFZQWwxMx/X/PqCw==", + "dev": true, + "requires": { + "workbox-core": "^3.6.3" + } + }, + "workbox-precaching": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-3.6.3.tgz", + "integrity": "sha512-aBqT66BuMFviPTW6IpccZZHzpA8xzvZU2OM1AdhmSlYDXOJyb1+Z6blVD7z2Q8VNtV1UVwQIdImIX+hH3C3PIw==", + "dev": true, + "requires": { + "workbox-core": "^3.6.3" + } + }, + "workbox-range-requests": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-3.6.3.tgz", + "integrity": "sha512-R+yLWQy7D9aRF9yJ3QzwYnGFnGDhMUij4jVBUVtkl67oaVoP1ymZ81AfCmfZro2kpPRI+vmNMfxxW531cqdx8A==", + "dev": true, + "requires": { + "workbox-core": "^3.6.3" + } + }, + "workbox-routing": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-3.6.3.tgz", + "integrity": "sha512-bX20i95OKXXQovXhFOViOK63HYmXvsIwZXKWbSpVeKToxMrp0G/6LZXnhg82ijj/S5yhKNRf9LeGDzaqxzAwMQ==", + "dev": true, + "requires": { + "workbox-core": "^3.6.3" + } + }, + "workbox-strategies": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-3.6.3.tgz", + "integrity": "sha512-Pg5eulqeKet2y8j73Yw6xTgLdElktcWExGkzDVCGqfV9JCvnGuEpz5eVsCIK70+k4oJcBCin9qEg3g3CwEIH3g==", + "dev": true, + "requires": { + "workbox-core": "^3.6.3" + } + }, + "workbox-streams": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-3.6.3.tgz", + "integrity": "sha512-rqDuS4duj+3aZUYI1LsrD2t9hHOjwPqnUIfrXSOxSVjVn83W2MisDF2Bj+dFUZv4GalL9xqErcFW++9gH+Z27w==", + "dev": true, + "requires": { + "workbox-core": "^3.6.3" + } + }, + "workbox-sw": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-3.6.3.tgz", + "integrity": "sha512-IQOUi+RLhvYCiv80RP23KBW/NTtIvzvjex28B8NW1jOm+iV4VIu3VXKXTA6er5/wjjuhmtB28qEAUqADLAyOSg==", + "dev": true + }, + "workbox-webpack-plugin": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-3.6.3.tgz", + "integrity": "sha512-RwmKjc7HFHUFHoOlKoZUq9349u0QN3F8W5tZZU0vc1qsBZDINWXRiIBCAKvo/Njgay5sWz7z4I2adnyTo97qIQ==", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "json-stable-stringify": "^1.0.1", + "workbox-build": "^3.6.3" + } + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "optional": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + }, + "dependencies": { + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + } + } + }, + "yorkie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yorkie/-/yorkie-2.0.0.tgz", + "integrity": "sha512-jcKpkthap6x63MB4TxwCyuIGkV0oYP/YRyuQU5UO0Yz/E/ZAu+653/uov+phdmO54n6BcvFRyyt0RRrWdN2mpw==", + "dev": true, + "requires": { + "execa": "^0.8.0", + "is-ci": "^1.0.10", + "normalize-path": "^1.0.0", + "strip-indent": "^2.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz", + "integrity": "sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "normalize-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-1.0.0.tgz", + "integrity": "sha1-MtDkcvkf80VwHBWoMRAY07CpA3k=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + } + } +} diff --git a/website/frontend/package.json b/website/frontend/package.json new file mode 100644 index 0000000..d1d336c --- /dev/null +++ b/website/frontend/package.json @@ -0,0 +1,63 @@ +{ + "name": "vue-typescript-admin-template", + "version": "0.1.0", + "private": true, + "author": "Chong Guo ", + "scripts": { + "serve": "vue-cli-service serve", + "build": "vue-cli-service build", + "lint": "vue-cli-service lint", + "svg": "vsvg -s ./src/icons/svg -t ./src/icons/components --ext ts --es6", + "test:e2e": "vue-cli-service test:e2e", + "test:unit": "vue-cli-service test:unit" + }, + "dependencies": { + "axios": "^0.19.0", + "element-ui": "^2.12.0", + "js-cookie": "^2.2.1", + "normalize.css": "^8.0.1", + "nprogress": "^0.2.0", + "path-to-regexp": "^3.0.0", + "register-service-worker": "^1.6.2", + "vue": "^2.6.10", + "vue-class-component": "^7.1.0", + "vue-property-decorator": "^8.2.2", + "vue-router": "^3.1.2", + "vue-svgicon": "^3.2.6", + "vuex": "^3.1.1", + "vuex-class": "^0.3.2", + "vuex-module-decorators": "^0.10.1" + }, + "devDependencies": { + "@types/jest": "^24.0.18", + "@types/js-cookie": "^2.2.2", + "@types/node": "~12.12.23", + "@types/nprogress": "^0.2.0", + "@types/webpack-env": "^1.14.0", + "@vue/cli-plugin-babel": "^3.11.0", + "@vue/cli-plugin-e2e-cypress": "^3.11.0", + "@vue/cli-plugin-eslint": "^3.11.0", + "@vue/cli-plugin-pwa": "^3.11.0", + "@vue/cli-plugin-typescript": "^3.11.0", + "@vue/cli-plugin-unit-jest": "^3.11.0", + "@vue/cli-service": "^3.11.0", + "@vue/eslint-config-standard": "^4.0.0", + "@vue/eslint-config-typescript": "^4.0.0", + "@vue/test-utils": "^1.0.0-beta.29", + "babel-core": "^7.0.0-bridge.0", + "babel-eslint": "^10.0.3", + "eslint": "^6.2.2", + "eslint-plugin-vue": "^5.2.3", + "fibers": "^4.0.1", + "jest": "^24.9.0", + "sass": "^1.22.10", + "sass-loader": "^7.3.1", + "style-resources-loader": "^1.2.1", + "ts-jest": "^24.0.2", + "typescript": "3.6.2", + "vue-cli-plugin-element": "^1.0.1", + "vue-cli-plugin-style-resources-loader": "^0.1.3", + "vue-template-compiler": "^2.6.10", + "webpack": "^4.39.3" + } +} diff --git a/website/frontend/postcss.config.js b/website/frontend/postcss.config.js new file mode 100644 index 0000000..961986e --- /dev/null +++ b/website/frontend/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + plugins: { + autoprefixer: {} + } +} diff --git a/website/frontend/public/favicon.ico b/website/frontend/public/favicon.ico new file mode 100644 index 0000000..a5a67cc Binary files /dev/null and b/website/frontend/public/favicon.ico differ diff --git a/website/frontend/public/img/icons/android-chrome-192x192.png b/website/frontend/public/img/icons/android-chrome-192x192.png new file mode 100644 index 0000000..6c882c4 Binary files /dev/null and b/website/frontend/public/img/icons/android-chrome-192x192.png differ diff --git a/website/frontend/public/img/icons/android-chrome-512x512.png b/website/frontend/public/img/icons/android-chrome-512x512.png new file mode 100644 index 0000000..e056f49 Binary files /dev/null and b/website/frontend/public/img/icons/android-chrome-512x512.png differ diff --git a/website/frontend/public/img/icons/apple-touch-icon-120x120.png b/website/frontend/public/img/icons/apple-touch-icon-120x120.png new file mode 100644 index 0000000..580c58a Binary files /dev/null and b/website/frontend/public/img/icons/apple-touch-icon-120x120.png differ diff --git a/website/frontend/public/img/icons/apple-touch-icon-152x152.png b/website/frontend/public/img/icons/apple-touch-icon-152x152.png new file mode 100644 index 0000000..ecdc132 Binary files /dev/null and b/website/frontend/public/img/icons/apple-touch-icon-152x152.png differ diff --git a/website/frontend/public/img/icons/apple-touch-icon-180x180.png b/website/frontend/public/img/icons/apple-touch-icon-180x180.png new file mode 100644 index 0000000..094ac70 Binary files /dev/null and b/website/frontend/public/img/icons/apple-touch-icon-180x180.png differ diff --git a/website/frontend/public/img/icons/apple-touch-icon-60x60.png b/website/frontend/public/img/icons/apple-touch-icon-60x60.png new file mode 100644 index 0000000..aebdaeb Binary files /dev/null and b/website/frontend/public/img/icons/apple-touch-icon-60x60.png differ diff --git a/website/frontend/public/img/icons/apple-touch-icon-76x76.png b/website/frontend/public/img/icons/apple-touch-icon-76x76.png new file mode 100644 index 0000000..04da253 Binary files /dev/null and b/website/frontend/public/img/icons/apple-touch-icon-76x76.png differ diff --git a/website/frontend/public/img/icons/apple-touch-icon.png b/website/frontend/public/img/icons/apple-touch-icon.png new file mode 100644 index 0000000..094ac70 Binary files /dev/null and b/website/frontend/public/img/icons/apple-touch-icon.png differ diff --git a/website/frontend/public/img/icons/favicon-16x16.png b/website/frontend/public/img/icons/favicon-16x16.png new file mode 100644 index 0000000..f11436d Binary files /dev/null and b/website/frontend/public/img/icons/favicon-16x16.png differ diff --git a/website/frontend/public/img/icons/favicon-32x32.png b/website/frontend/public/img/icons/favicon-32x32.png new file mode 100644 index 0000000..7c3d0e7 Binary files /dev/null and b/website/frontend/public/img/icons/favicon-32x32.png differ diff --git a/website/frontend/public/img/icons/msapplication-icon-144x144.png b/website/frontend/public/img/icons/msapplication-icon-144x144.png new file mode 100644 index 0000000..843dcd1 Binary files /dev/null and b/website/frontend/public/img/icons/msapplication-icon-144x144.png differ diff --git a/website/frontend/public/img/icons/mstile-150x150.png b/website/frontend/public/img/icons/mstile-150x150.png new file mode 100644 index 0000000..a8e8654 Binary files /dev/null and b/website/frontend/public/img/icons/mstile-150x150.png differ diff --git a/website/frontend/public/img/icons/safari-pinned-tab.svg b/website/frontend/public/img/icons/safari-pinned-tab.svg new file mode 100644 index 0000000..673e7b0 --- /dev/null +++ b/website/frontend/public/img/icons/safari-pinned-tab.svg @@ -0,0 +1,112 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + + diff --git a/website/frontend/public/index.html b/website/frontend/public/index.html new file mode 100644 index 0000000..06d696c --- /dev/null +++ b/website/frontend/public/index.html @@ -0,0 +1,17 @@ + + + + + + + + <%= webpackConfig.name %> + + + +
+ + + diff --git a/website/frontend/public/manifest.json b/website/frontend/public/manifest.json new file mode 100644 index 0000000..67ada24 --- /dev/null +++ b/website/frontend/public/manifest.json @@ -0,0 +1,20 @@ +{ + "name": "SWT Admin", + "short_name": "SWT Admin", + "icons": [ + { + "src": "./img/icons/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "./img/icons/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "start_url": "./index.html", + "display": "standalone", + "background_color": "#fff", + "theme_color": "#4DBA87" +} diff --git a/website/frontend/public/robots.txt b/website/frontend/public/robots.txt new file mode 100644 index 0000000..eb05362 --- /dev/null +++ b/website/frontend/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/website/frontend/src/App.vue b/website/frontend/src/App.vue new file mode 100644 index 0000000..9d213d1 --- /dev/null +++ b/website/frontend/src/App.vue @@ -0,0 +1,14 @@ + + + diff --git a/website/frontend/src/api/articles.ts b/website/frontend/src/api/articles.ts new file mode 100644 index 0000000..da99da4 --- /dev/null +++ b/website/frontend/src/api/articles.ts @@ -0,0 +1,8 @@ +import request from '@/utils/request' + +export const getArticles = (params: any) => + request({ + url: '/articles', + method: 'get', + params + }) diff --git a/website/frontend/src/api/node.ts b/website/frontend/src/api/node.ts new file mode 100644 index 0000000..c35b7ad --- /dev/null +++ b/website/frontend/src/api/node.ts @@ -0,0 +1,23 @@ +import request from '@/utils/request' + +export const getNodeList = async (agents: string[]) => { + let data = { agents: agents } + let ret = await request({ + url: '/api/agent_list', + method: 'post', + data + }) + + return ret.data as Define.Node[] +} + +export const getNodeServices = async (agents: string[]) => { + let data = { agents: agents } + let ret = await request({ + url: '/api/agent_services', + method: 'post', + data + }) + + return ret.data +} diff --git a/website/frontend/src/api/types.d.ts b/website/frontend/src/api/types.d.ts new file mode 100644 index 0000000..d7e346c --- /dev/null +++ b/website/frontend/src/api/types.d.ts @@ -0,0 +1,17 @@ +export interface IArticleData { + id: number + status: string + title: string + abstractContent: string + fullContent: string + sourceURL: string + imageURL: string + timestamp: string | number + platforms: string[] + disableComment: boolean + importance: number + author: string + reviewer: string + type: string + pageviews: number +} diff --git a/website/frontend/src/api/users.ts b/website/frontend/src/api/users.ts new file mode 100644 index 0000000..81e0e9e --- /dev/null +++ b/website/frontend/src/api/users.ts @@ -0,0 +1,21 @@ +import request from '@/utils/request' + +export const getUserInfo = (data: any) => + request({ + url: '/users/info', + method: 'post', + data + }) + +export const login = (data: any) => + request({ + url: '/users/login', + method: 'post', + data + }) + +export const logout = () => + request({ + url: '/users/logout', + method: 'post' + }) diff --git a/website/frontend/src/assets/404-images/404-cloud.png b/website/frontend/src/assets/404-images/404-cloud.png new file mode 100644 index 0000000..c6281d0 Binary files /dev/null and b/website/frontend/src/assets/404-images/404-cloud.png differ diff --git a/website/frontend/src/assets/404-images/404.png b/website/frontend/src/assets/404-images/404.png new file mode 100644 index 0000000..3d8e230 Binary files /dev/null and b/website/frontend/src/assets/404-images/404.png differ diff --git a/website/frontend/src/components/Breadcrumb/index.vue b/website/frontend/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..430a78c --- /dev/null +++ b/website/frontend/src/components/Breadcrumb/index.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/website/frontend/src/components/Hamburger/index.vue b/website/frontend/src/components/Hamburger/index.vue new file mode 100644 index 0000000..5a28314 --- /dev/null +++ b/website/frontend/src/components/Hamburger/index.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/website/frontend/src/define.d.ts b/website/frontend/src/define.d.ts new file mode 100644 index 0000000..65cb188 --- /dev/null +++ b/website/frontend/src/define.d.ts @@ -0,0 +1,14 @@ +declare namespace Define { + type Node = { + addr: string + type: string + name: string + status: string + } + + type LuaService = { + node?: Node + addr: string + name: string + } +} diff --git a/website/frontend/src/icons/README.md b/website/frontend/src/icons/README.md new file mode 100644 index 0000000..9f375c5 --- /dev/null +++ b/website/frontend/src/icons/README.md @@ -0,0 +1,13 @@ +# vue-svgicon + +## English + +* All svg components were generated by `vue-svgicon` using svg files +* After you adding new svg files into `icons/svg` folder, run `yarn svg` to regerenrate all svg components (before this, you should have `vue-svgicon` installed globally or use `npx`) +* See details at: [https://github.com/MMF-FE/vue-svgicon](https://github.com/MMF-FE/vue-svgicon) + +## 中文 + +* 所有的 svg 组件都是由 `vue-svgicon` 生成的 +* 每当在 `icons/svg` 文件夹内添加 icon 之后,可以通过执行 `yarn svg` 来重新生成所有组件 (在此之前需要全局安装 `vue-svgicon` 或使用 `npx`) +* 详细文档请见:[https://github.com/MMF-FE/vue-svgicon](https://github.com/MMF-FE/vue-svgicon) diff --git a/website/frontend/src/icons/components/dashboard.ts b/website/frontend/src/icons/components/dashboard.ts new file mode 100644 index 0000000..c4aae73 --- /dev/null +++ b/website/frontend/src/icons/components/dashboard.ts @@ -0,0 +1,12 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-ignore +import icon from 'vue-svgicon' +icon.register({ + 'dashboard': { + width: 128, + height: 100, + viewBox: '0 0 128 100', + data: '' + } +}) diff --git a/website/frontend/src/icons/components/example.ts b/website/frontend/src/icons/components/example.ts new file mode 100644 index 0000000..17bf38b --- /dev/null +++ b/website/frontend/src/icons/components/example.ts @@ -0,0 +1,12 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-ignore +import icon from 'vue-svgicon' +icon.register({ + 'example': { + width: 128, + height: 128, + viewBox: '0 0 128 128', + data: '' + } +}) diff --git a/website/frontend/src/icons/components/eye-off.ts b/website/frontend/src/icons/components/eye-off.ts new file mode 100644 index 0000000..1bf11d6 --- /dev/null +++ b/website/frontend/src/icons/components/eye-off.ts @@ -0,0 +1,12 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-ignore +import icon from 'vue-svgicon' +icon.register({ + 'eye-off': { + width: 128, + height: 64, + viewBox: '0 0 128 64', + data: '' + } +}) diff --git a/website/frontend/src/icons/components/eye-on.ts b/website/frontend/src/icons/components/eye-on.ts new file mode 100644 index 0000000..7d20213 --- /dev/null +++ b/website/frontend/src/icons/components/eye-on.ts @@ -0,0 +1,12 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-ignore +import icon from 'vue-svgicon' +icon.register({ + 'eye-on': { + width: 128, + height: 128, + viewBox: '0 0 1024 1024', + data: '' + } +}) diff --git a/website/frontend/src/icons/components/form.ts b/website/frontend/src/icons/components/form.ts new file mode 100644 index 0000000..48d561f --- /dev/null +++ b/website/frontend/src/icons/components/form.ts @@ -0,0 +1,12 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-ignore +import icon from 'vue-svgicon' +icon.register({ + 'form': { + width: 128, + height: 128, + viewBox: '0 0 128 128', + data: '' + } +}) diff --git a/website/frontend/src/icons/components/hamburger.ts b/website/frontend/src/icons/components/hamburger.ts new file mode 100644 index 0000000..8bbecdf --- /dev/null +++ b/website/frontend/src/icons/components/hamburger.ts @@ -0,0 +1,12 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-ignore +import icon from 'vue-svgicon' +icon.register({ + 'hamburger': { + width: 64, + height: 64, + viewBox: '0 0 1024 1024', + data: '' + } +}) diff --git a/website/frontend/src/icons/components/index.ts b/website/frontend/src/icons/components/index.ts new file mode 100644 index 0000000..932d5c0 --- /dev/null +++ b/website/frontend/src/icons/components/index.ts @@ -0,0 +1,13 @@ +/* tslint:disable */ +import './dashboard' +import './example' +import './eye-off' +import './eye-on' +import './form' +import './hamburger' +import './link' +import './nested' +import './password' +import './table' +import './tree' +import './user' diff --git a/website/frontend/src/icons/components/link.ts b/website/frontend/src/icons/components/link.ts new file mode 100644 index 0000000..cade193 --- /dev/null +++ b/website/frontend/src/icons/components/link.ts @@ -0,0 +1,12 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-ignore +import icon from 'vue-svgicon' +icon.register({ + 'link': { + width: 128, + height: 128, + viewBox: '0 0 128 128', + data: '' + } +}) diff --git a/website/frontend/src/icons/components/nested.ts b/website/frontend/src/icons/components/nested.ts new file mode 100644 index 0000000..796ef0c --- /dev/null +++ b/website/frontend/src/icons/components/nested.ts @@ -0,0 +1,12 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-ignore +import icon from 'vue-svgicon' +icon.register({ + 'nested': { + width: 128, + height: 128, + viewBox: '0 0 128 128', + data: '' + } +}) diff --git a/website/frontend/src/icons/components/password.ts b/website/frontend/src/icons/components/password.ts new file mode 100644 index 0000000..f76175e --- /dev/null +++ b/website/frontend/src/icons/components/password.ts @@ -0,0 +1,12 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-ignore +import icon from 'vue-svgicon' +icon.register({ + 'password': { + width: 128, + height: 128, + viewBox: '0 0 128 128', + data: '' + } +}) diff --git a/website/frontend/src/icons/components/table.ts b/website/frontend/src/icons/components/table.ts new file mode 100644 index 0000000..f08c8ed --- /dev/null +++ b/website/frontend/src/icons/components/table.ts @@ -0,0 +1,12 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-ignore +import icon from 'vue-svgicon' +icon.register({ + 'table': { + width: 128, + height: 128, + viewBox: '0 0 128 128', + data: '' + } +}) diff --git a/website/frontend/src/icons/components/tree.ts b/website/frontend/src/icons/components/tree.ts new file mode 100644 index 0000000..735bc44 --- /dev/null +++ b/website/frontend/src/icons/components/tree.ts @@ -0,0 +1,12 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-ignore +import icon from 'vue-svgicon' +icon.register({ + 'tree': { + width: 128, + height: 128, + viewBox: '0 0 128 128', + data: '' + } +}) diff --git a/website/frontend/src/icons/components/user.ts b/website/frontend/src/icons/components/user.ts new file mode 100644 index 0000000..015f145 --- /dev/null +++ b/website/frontend/src/icons/components/user.ts @@ -0,0 +1,12 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-ignore +import icon from 'vue-svgicon' +icon.register({ + 'user': { + width: 130, + height: 130, + viewBox: '0 0 130 130', + data: '' + } +}) diff --git a/website/frontend/src/icons/svg/dashboard.svg b/website/frontend/src/icons/svg/dashboard.svg new file mode 100644 index 0000000..af3d90a --- /dev/null +++ b/website/frontend/src/icons/svg/dashboard.svg @@ -0,0 +1,3 @@ + + + diff --git a/website/frontend/src/icons/svg/example.svg b/website/frontend/src/icons/svg/example.svg new file mode 100644 index 0000000..bd41f92 --- /dev/null +++ b/website/frontend/src/icons/svg/example.svg @@ -0,0 +1,3 @@ + + + diff --git a/website/frontend/src/icons/svg/eye-off.svg b/website/frontend/src/icons/svg/eye-off.svg new file mode 100644 index 0000000..51b87f6 --- /dev/null +++ b/website/frontend/src/icons/svg/eye-off.svg @@ -0,0 +1,3 @@ + + + diff --git a/website/frontend/src/icons/svg/eye-on.svg b/website/frontend/src/icons/svg/eye-on.svg new file mode 100644 index 0000000..6a92959 --- /dev/null +++ b/website/frontend/src/icons/svg/eye-on.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/website/frontend/src/icons/svg/form.svg b/website/frontend/src/icons/svg/form.svg new file mode 100644 index 0000000..4f07a47 --- /dev/null +++ b/website/frontend/src/icons/svg/form.svg @@ -0,0 +1,3 @@ + + + diff --git a/website/frontend/src/icons/svg/hamburger.svg b/website/frontend/src/icons/svg/hamburger.svg new file mode 100644 index 0000000..03857d7 --- /dev/null +++ b/website/frontend/src/icons/svg/hamburger.svg @@ -0,0 +1,3 @@ + + + diff --git a/website/frontend/src/icons/svg/link.svg b/website/frontend/src/icons/svg/link.svg new file mode 100644 index 0000000..4853191 --- /dev/null +++ b/website/frontend/src/icons/svg/link.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/website/frontend/src/icons/svg/nested.svg b/website/frontend/src/icons/svg/nested.svg new file mode 100644 index 0000000..6a4c16d --- /dev/null +++ b/website/frontend/src/icons/svg/nested.svg @@ -0,0 +1,3 @@ + + + diff --git a/website/frontend/src/icons/svg/password.svg b/website/frontend/src/icons/svg/password.svg new file mode 100644 index 0000000..4c66ecf --- /dev/null +++ b/website/frontend/src/icons/svg/password.svg @@ -0,0 +1,3 @@ + + + diff --git a/website/frontend/src/icons/svg/table.svg b/website/frontend/src/icons/svg/table.svg new file mode 100644 index 0000000..f7a4337 --- /dev/null +++ b/website/frontend/src/icons/svg/table.svg @@ -0,0 +1,4 @@ + + + + diff --git a/website/frontend/src/icons/svg/tree.svg b/website/frontend/src/icons/svg/tree.svg new file mode 100644 index 0000000..3432350 --- /dev/null +++ b/website/frontend/src/icons/svg/tree.svg @@ -0,0 +1,3 @@ + + + diff --git a/website/frontend/src/icons/svg/user.svg b/website/frontend/src/icons/svg/user.svg new file mode 100644 index 0000000..356d5dd --- /dev/null +++ b/website/frontend/src/icons/svg/user.svg @@ -0,0 +1,3 @@ + + + diff --git a/website/frontend/src/layout/components/AppMain.vue b/website/frontend/src/layout/components/AppMain.vue new file mode 100644 index 0000000..27e336b --- /dev/null +++ b/website/frontend/src/layout/components/AppMain.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/website/frontend/src/layout/components/Navbar/index.vue b/website/frontend/src/layout/components/Navbar/index.vue new file mode 100644 index 0000000..2d61c9f --- /dev/null +++ b/website/frontend/src/layout/components/Navbar/index.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/website/frontend/src/layout/components/Sidebar/SidebarItem.vue b/website/frontend/src/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 0000000..7557461 --- /dev/null +++ b/website/frontend/src/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,176 @@ + + + + + + + diff --git a/website/frontend/src/layout/components/Sidebar/SidebarItemLink.vue b/website/frontend/src/layout/components/Sidebar/SidebarItemLink.vue new file mode 100644 index 0000000..56c5f3a --- /dev/null +++ b/website/frontend/src/layout/components/Sidebar/SidebarItemLink.vue @@ -0,0 +1,30 @@ + + + diff --git a/website/frontend/src/layout/components/Sidebar/index.vue b/website/frontend/src/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..b48d579 --- /dev/null +++ b/website/frontend/src/layout/components/Sidebar/index.vue @@ -0,0 +1,91 @@ + + + + + + + diff --git a/website/frontend/src/layout/components/index.ts b/website/frontend/src/layout/components/index.ts new file mode 100644 index 0000000..d34baeb --- /dev/null +++ b/website/frontend/src/layout/components/index.ts @@ -0,0 +1,3 @@ +export { default as AppMain } from './AppMain.vue' +export { default as Navbar } from './Navbar/index.vue' +export { default as Sidebar } from './Sidebar/index.vue' diff --git a/website/frontend/src/layout/index.vue b/website/frontend/src/layout/index.vue new file mode 100644 index 0000000..106dafe --- /dev/null +++ b/website/frontend/src/layout/index.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/website/frontend/src/layout/mixin/resize.ts b/website/frontend/src/layout/mixin/resize.ts new file mode 100644 index 0000000..69df007 --- /dev/null +++ b/website/frontend/src/layout/mixin/resize.ts @@ -0,0 +1,55 @@ +import { Component, Vue, Watch } from 'vue-property-decorator' +import { AppModule, DeviceType } from '@/store/modules/app' + +const WIDTH = 992 // refer to Bootstrap's responsive design + +@Component({ + name: 'ResizeMixin' +}) +export default class extends Vue { + get device() { + return AppModule.device + } + + get sidebar() { + return AppModule.sidebar + } + + @Watch('$route') + private onRouteChange() { + if (this.device === DeviceType.Mobile && this.sidebar.opened) { + AppModule.CloseSideBar(false) + } + } + + beforeMount() { + window.addEventListener('resize', this.resizeHandler) + } + + mounted() { + const isMobile = this.isMobile() + if (isMobile) { + AppModule.ToggleDevice(DeviceType.Mobile) + AppModule.CloseSideBar(true) + } + } + + beforeDestroy() { + window.removeEventListener('resize', this.resizeHandler) + } + + private isMobile() { + const rect = document.body.getBoundingClientRect() + return rect.width - 1 < WIDTH + } + + private resizeHandler() { + if (!document.hidden) { + const isMobile = this.isMobile() + AppModule.ToggleDevice(isMobile ? DeviceType.Mobile : DeviceType.Desktop) + if (isMobile) { + AppModule.CloseSideBar(true) + } + } + } +} diff --git a/website/frontend/src/main.ts b/website/frontend/src/main.ts new file mode 100644 index 0000000..6e6ae0c --- /dev/null +++ b/website/frontend/src/main.ts @@ -0,0 +1,29 @@ +import Vue from 'vue' + +import 'normalize.css' +import ElementUI from 'element-ui' +import SvgIcon from 'vue-svgicon' + +import '@/styles/element-variables.scss' +import '@/styles/index.scss' + +import App from '@/App.vue' +import store from '@/store' +import router from '@/router' +import '@/icons/components' +import '@/permission' + +Vue.use(ElementUI) +Vue.use(SvgIcon, { + tagName: 'svg-icon', + defaultWidth: '1em', + defaultHeight: '1em' +}) + +Vue.config.productionTip = false + +new Vue({ + router, + store, + render: (h) => h(App) +}).$mount('#app') diff --git a/website/frontend/src/permission.ts b/website/frontend/src/permission.ts new file mode 100644 index 0000000..f9a89df --- /dev/null +++ b/website/frontend/src/permission.ts @@ -0,0 +1,62 @@ +import router from './router' +import NProgress from 'nprogress' +import 'nprogress/nprogress.css' +import { Message } from 'element-ui' +import { Route } from 'vue-router' +import { UserModule } from '@/store/modules/user' + +NProgress.configure({ showSpinner: false }) + +const whiteList = ['/login'] + +/* +router.beforeEach(async(to: Route, _: Route, next: any) => { + // Start progress bar + NProgress.start() + + // Determine whether the user has logged in + if (UserModule.token) { + if (to.path === '/login') { + // If is logged in, redirect to the home page + next({ path: '/' }) + NProgress.done() + } else { + // Check whether the user has obtained his permission roles + if (UserModule.roles.length === 0) { + try { + // Get user info, including roles + await UserModule.GetUserInfo() + // Set the replace: true, so the navigation will not leave a history record + next({ ...to, replace: true }) + } catch (err) { + // Remove token and redirect to login page + UserModule.ResetToken() + Message.error(err || 'Has Error') + next(`/login?redirect=${to.path}`) + NProgress.done() + } + } else { + next() + } + } + } else { + // Has no token + if (whiteList.indexOf(to.path) !== -1) { + // In the free login whitelist, go directly + next() + } else { + // Other pages that do not have permission to access are redirected to the login page. + next(`/login?redirect=${to.path}`) + NProgress.done() + } + } +}) +*/ + +router.afterEach((to: Route) => { + // Finish progress bar + NProgress.done() + + // set page title + document.title = to.meta.title +}) diff --git a/website/frontend/src/registerServiceWorker.ts b/website/frontend/src/registerServiceWorker.ts new file mode 100644 index 0000000..7ac6399 --- /dev/null +++ b/website/frontend/src/registerServiceWorker.ts @@ -0,0 +1,32 @@ +/* eslint-disable no-console */ + +import { register } from 'register-service-worker' + +if (process.env.NODE_ENV === 'production') { + register(`${process.env.BASE_URL}service-worker.js`, { + ready() { + console.log( + 'App is being served from cache by a service worker.\n' + + 'For more details, visit https://goo.gl/AFskqB' + ) + }, + registered() { + console.log('Service worker has been registered.') + }, + cached() { + console.log('Content has been cached for offline use.') + }, + updatefound() { + console.log('New content is downloading.') + }, + updated() { + console.log('New content is available; please refresh.') + }, + offline() { + console.log('No internet connection found. App is running in offline mode.') + }, + error(error) { + console.error('Error during service worker registration:', error) + } + }) +} diff --git a/website/frontend/src/router.ts b/website/frontend/src/router.ts new file mode 100644 index 0000000..b74ccbb --- /dev/null +++ b/website/frontend/src/router.ts @@ -0,0 +1,89 @@ +import Vue from 'vue' +import Router from 'vue-router' +import Layout from '@/layout/index.vue' + +Vue.use(Router) + +/* + redirect: if set to 'noredirect', no redirect action will be trigger when clicking the breadcrumb + meta: { + title: 'title' the name showed in subMenu and breadcrumb (recommend set) + icon: 'svg-name' the icon showed in the sidebar + breadcrumb: false if false, the item will be hidden in breadcrumb (default is true) + hidden: true if true, this route will not show in the sidebar (default is false) + } +*/ + +export default new Router({ + // mode: 'history', // Enable this if you need. + scrollBehavior: (to, from, savedPosition) => { + if (savedPosition) { + return savedPosition + } else { + return { x: 0, y: 0 } + } + }, + base: process.env.BASE_URL, + routes: [ + { + path: '/login', + component: () => import(/* webpackChunkName: "login" */ '@/views/login/index.vue'), + meta: { hidden: true } + }, + { + path: '/404', + component: () => import(/* webpackChunkName: "404" */ '@/views/404.vue'), + meta: { hidden: true } + }, + { + path: '/', + component: Layout, + redirect: '/dashboard', + children: [ + { + path: 'dashboard', + component: () => import(/* webpackChunkName: "dashboard" */ '@/views/dashboard/index.vue'), + meta: { + title: 'dashboard', + icon: 'dashboard' + } + } + ] + }, + { + path: '/', + component: Layout, + redirect: '/debug', + children: [ + { + path: 'debug', + component: () => import(/* webpackChunkName: "Debug" */ '@/views/debug/index.vue'), + meta: { + title: 'Debug', + icon: 'example' + } + } + ] + }, + { + path: '/', + component: Layout, + redirect: '/profiler', + children: [ + { + path: 'profiler', + component: () => import(/* webpackChunkName: "Profiler" */ '@/views/profiler/index.vue'), + meta: { + title: 'Profiler', + icon: 'example' + } + } + ] + }, + { + path: '*', + redirect: '/404', + meta: { hidden: true } + } + ] +}) diff --git a/website/frontend/src/shims-vue.d.ts b/website/frontend/src/shims-vue.d.ts new file mode 100644 index 0000000..d9f24fa --- /dev/null +++ b/website/frontend/src/shims-vue.d.ts @@ -0,0 +1,4 @@ +declare module '*.vue' { + import Vue from 'vue' + export default Vue +} diff --git a/website/frontend/src/store/index.ts b/website/frontend/src/store/index.ts new file mode 100644 index 0000000..83c726c --- /dev/null +++ b/website/frontend/src/store/index.ts @@ -0,0 +1,14 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import { IAppState } from './modules/app' +import { IUserState } from './modules/user' + +Vue.use(Vuex) + +export interface IRootState { + app: IAppState + user: IUserState +} + +// Declare empty store first, dynamically register all modules later. +export default new Vuex.Store({}) diff --git a/website/frontend/src/store/modules/app.ts b/website/frontend/src/store/modules/app.ts new file mode 100644 index 0000000..7a57eb7 --- /dev/null +++ b/website/frontend/src/store/modules/app.ts @@ -0,0 +1,65 @@ +import { VuexModule, Module, Mutation, Action, getModule } from 'vuex-module-decorators' +import { getSidebarStatus, setSidebarStatus } from '@/utils/cookies' +import store from '@/store' + +export enum DeviceType { + Mobile, + Desktop, +} + +export interface IAppState { + device: DeviceType + sidebar: { + opened: boolean + withoutAnimation: boolean + } +} + +@Module({ dynamic: true, store, name: 'app' }) +class App extends VuexModule implements IAppState { + public sidebar = { + opened: getSidebarStatus() !== 'closed', + withoutAnimation: false + } + public device = DeviceType.Desktop + + @Mutation + private TOGGLE_SIDEBAR(withoutAnimation: boolean) { + this.sidebar.opened = !this.sidebar.opened + this.sidebar.withoutAnimation = withoutAnimation + if (this.sidebar.opened) { + setSidebarStatus('opened') + } else { + setSidebarStatus('closed') + } + } + + @Mutation + private CLOSE_SIDEBAR(withoutAnimation: boolean) { + this.sidebar.opened = false + this.sidebar.withoutAnimation = withoutAnimation + setSidebarStatus('closed') + } + + @Mutation + private TOGGLE_DEVICE(device: DeviceType) { + this.device = device + } + + @Action + public ToggleSideBar(withoutAnimation: boolean) { + this.TOGGLE_SIDEBAR(withoutAnimation) + } + + @Action + public CloseSideBar(withoutAnimation: boolean) { + this.CLOSE_SIDEBAR(withoutAnimation) + } + + @Action + public ToggleDevice(device: DeviceType) { + this.TOGGLE_DEVICE(device) + } +} + +export const AppModule = getModule(App) diff --git a/website/frontend/src/store/modules/user.ts b/website/frontend/src/store/modules/user.ts new file mode 100644 index 0000000..4485df6 --- /dev/null +++ b/website/frontend/src/store/modules/user.ts @@ -0,0 +1,95 @@ +import { VuexModule, Module, Action, Mutation, getModule } from 'vuex-module-decorators' +import { login, logout, getUserInfo } from '@/api/users' +import { getToken, setToken, removeToken } from '@/utils/cookies' +import store from '@/store' + +export interface IUserState { + token: string + name: string + avatar: string + introduction: string + roles: string[] +} + +@Module({ dynamic: true, store, name: 'user' }) +class User extends VuexModule implements IUserState { + public token = getToken() || '' + public name = '' + public avatar = '' + public introduction = '' + public roles: string[] = [] + + @Mutation + private SET_TOKEN(token: string) { + this.token = token + } + + @Mutation + private SET_NAME(name: string) { + this.name = name + } + + @Mutation + private SET_AVATAR(avatar: string) { + this.avatar = avatar + } + + @Mutation + private SET_INTRODUCTION(introduction: string) { + this.introduction = introduction + } + + @Mutation + private SET_ROLES(roles: string[]) { + this.roles = roles + } + + @Action + public async Login(userInfo: { username: string, password: string }) { + let { username, password } = userInfo + username = username.trim() + const { data } = await login({ username, password }) + setToken(data.accessToken) + this.SET_TOKEN(data.accessToken) + } + + @Action + public ResetToken() { + removeToken() + this.SET_TOKEN('') + this.SET_ROLES([]) + } + + @Action + public async GetUserInfo() { + if (this.token === '') { + throw Error('GetUserInfo: token is undefined!') + } + const { data } = await getUserInfo({ /* Your params here */ }) + if (!data) { + throw Error('Verification failed, please Login again.') + } + const { roles, name, avatar, introduction } = data.user + // roles must be a non-empty array + if (!roles || roles.length <= 0) { + throw Error('GetUserInfo: roles must be a non-null array!') + } + this.SET_ROLES(roles) + this.SET_NAME(name) + this.SET_AVATAR(avatar) + this.SET_INTRODUCTION(introduction) + } + + @Action + public async LogOut() { + if (this.token === '') { + throw Error('LogOut: token is undefined!') + } + await logout() + removeToken() + this.SET_TOKEN('') + this.SET_ROLES([]) + } +} + +export const UserModule = getModule(User) diff --git a/website/frontend/src/styles/_mixins.scss b/website/frontend/src/styles/_mixins.scss new file mode 100644 index 0000000..50048ad --- /dev/null +++ b/website/frontend/src/styles/_mixins.scss @@ -0,0 +1,8 @@ +/* Mixins */ +@mixin clearfix { + &:after { + content: ""; + display: table; + clear: both; + } +} diff --git a/website/frontend/src/styles/_svgicon.scss b/website/frontend/src/styles/_svgicon.scss new file mode 100644 index 0000000..535b50e --- /dev/null +++ b/website/frontend/src/styles/_svgicon.scss @@ -0,0 +1,31 @@ +/* Recommended css code for vue-svgicon */ +.svg-icon { + display: inline-block; + width: 16px; + height: 16px; + color: inherit; + fill: none; + stroke: currentColor; + vertical-align: -0.15em; +} + +.svg-fill { + fill: currentColor; + stroke: none; +} + +.svg-up { + transform: rotate(0deg); +} + +.svg-right { + transform: rotate(90deg); +} + +.svg-down { + transform: rotate(180deg); +} + +.svg-left { + transform: rotate(-90deg); +} diff --git a/website/frontend/src/styles/_transition.scss b/website/frontend/src/styles/_transition.scss new file mode 100644 index 0000000..b3bef8a --- /dev/null +++ b/website/frontend/src/styles/_transition.scss @@ -0,0 +1,49 @@ +/* Global transition */ +// See https://vuejs.org/v2/guide/transitions.html for detail + +// fade +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.28s; +} + +.fade-enter, +.fade-leave-active { + opacity: 0; +} + +// fade-transform +.fade-transform-leave-active, +.fade-transform-enter-active { + transition: all .5s; +} + +.fade-transform-enter { + opacity: 0; + transform: translateX(-30px); +} + +.fade-transform-leave-to { + opacity: 0; + transform: translateX(30px); +} + +// breadcrumb +.breadcrumb-enter-active, +.breadcrumb-leave-active { + transition: all .5s; +} + +.breadcrumb-enter, +.breadcrumb-leave-active { + opacity: 0; + transform: translateX(20px); +} + +.breadcrumb-move { + transition: all .5s; +} + +.breadcrumb-leave-active { + position: absolute; +} diff --git a/website/frontend/src/styles/_variables.scss b/website/frontend/src/styles/_variables.scss new file mode 100644 index 0000000..e4ee3e0 --- /dev/null +++ b/website/frontend/src/styles/_variables.scss @@ -0,0 +1,34 @@ +/* Variables */ + +// Base color +$blue:#324157; +$light-blue:#3A71A8; +$red:#C03639; +$pink: #E65D6E; +$green: #30B08F; +$tiffany: #4AB7BD; +$yellow:#FEC171; +$panGreen: #30B08F; + +// Sidebar +$sideBarWidth: 210px; +$subMenuBg:#1f2d3d; +$subMenuHover:#001528; +$subMenuActiveText:#f4f4f5; +$menuBg:#304156; +$menuText:#bfcbd9; +$menuActiveText:#409EFF; // Also see settings.sidebarTextTheme + +// Login page +$lightGray: #eee; +$darkGray:#889aa4; +$loginBg: #2d3a4b; +$loginCursorColor: #fff; + +// The :export directive is the magic sauce for webpack +// https://mattferderer.com/use-sass-variables-in-typescript-and-javascript +:export { + menuBg: $menuBg; + menuText: $menuText; + menuActiveText: $menuActiveText; +} diff --git a/website/frontend/src/styles/_variables.scss.d.ts b/website/frontend/src/styles/_variables.scss.d.ts new file mode 100644 index 0000000..f8a728e --- /dev/null +++ b/website/frontend/src/styles/_variables.scss.d.ts @@ -0,0 +1,9 @@ +export interface IScssVariables { + menuBg: string + menuText: string + menuActiveText: string +} + +export const variables: IScssVariables + +export default variables diff --git a/website/frontend/src/styles/element-variables.scss b/website/frontend/src/styles/element-variables.scss new file mode 100644 index 0000000..598b5d9 --- /dev/null +++ b/website/frontend/src/styles/element-variables.scss @@ -0,0 +1,19 @@ +/* Element Variables */ + +// Override Element UI variables +$--color-primary: #1890ff; +$--color-success: #13ce66; +$--color-warning: #FFBA00; +$--color-danger: #ff4949; +$--color-info: #5d5d5d; +$--button-font-weight: 400; +$--color-text-regular: #1f2d3d; +$--border-color-light: #dfe4ed; +$--border-color-lighter: #e6ebf5; +$--table-border:1px solid#dfe6ec; + +// Icon font path, required +$--font-path: '~element-ui/lib/theme-chalk/fonts'; + +// Apply overrided variables in Element UI +@import '~element-ui/packages/theme-chalk/src/index'; diff --git a/website/frontend/src/styles/index.scss b/website/frontend/src/styles/index.scss new file mode 100644 index 0000000..3d30082 --- /dev/null +++ b/website/frontend/src/styles/index.scss @@ -0,0 +1,47 @@ +// @import './variables.scss'; // Already imported in style-resources-loader +// @import './mixins.scss'; // Already imported in style-resources-loader +@import './transition.scss'; +@import './svgicon.scss'; + +/* Global scss */ + +body { + height: 100%; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; +} + +html { + height: 100%; +} + +#app { + height: 100%; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +a, +a:focus, +a:hover { + color: inherit; + outline: none; + text-decoration: none; +} + +div:focus { + outline: none; +} + +.clearfix { + @include clearfix; +} + +.app-container { + padding: 20px; +} diff --git a/website/frontend/src/utils/cookies.ts b/website/frontend/src/utils/cookies.ts new file mode 100644 index 0000000..d91ae81 --- /dev/null +++ b/website/frontend/src/utils/cookies.ts @@ -0,0 +1,12 @@ +import Cookies from 'js-cookie' + +// App +const sidebarStatusKey = 'sidebar_status' +export const getSidebarStatus = () => Cookies.get(sidebarStatusKey) +export const setSidebarStatus = (sidebarStatus: string) => Cookies.set(sidebarStatusKey, sidebarStatus) + +// User +const tokenKey = 'vue_typescript_admin_access_token' +export const getToken = () => Cookies.get(tokenKey) +export const setToken = (token: string) => Cookies.set(tokenKey, token) +export const removeToken = () => Cookies.remove(tokenKey) diff --git a/website/frontend/src/utils/request.ts b/website/frontend/src/utils/request.ts new file mode 100644 index 0000000..e372741 --- /dev/null +++ b/website/frontend/src/utils/request.ts @@ -0,0 +1,71 @@ +import axios from 'axios' +import { Message, MessageBox } from 'element-ui' +import { UserModule } from '@/store/modules/user' + +const service = axios.create({ + baseURL: process.env.VUE_APP_BASE_API, + timeout: 5000 +}) + +// Request interceptors +service.interceptors.request.use( + (config) => { + // Add X-Access-Token header to every request, you can add other custom headers here + if (UserModule.token) { + config.headers['X-Access-Token'] = UserModule.token + } + return config + }, + (error) => { + Promise.reject(error) + } +) + +// Response interceptors +service.interceptors.response.use( + (response) => { + // Some example codes here: + // code == 20000: success + // code == 50001: invalid access token + // code == 50002: already login in other place + // code == 50003: access token expired + // code == 50004: invalid user (user not exist) + // code == 50005: username or password is incorrect + // You can change this part for your own usage. + const res = response.data + if (res.code !== 20000) { + Message({ + message: res.message || 'Error', + type: 'error', + duration: 5 * 1000 + }) + if (res.code === 50008 || res.code === 50012 || res.code === 50014) { + MessageBox.confirm( + 'You have been logged out, try to login again.', + 'Log out', + { + confirmButtonText: 'Relogin', + cancelButtonText: 'Cancel', + type: 'warning' + } + ).then(() => { + UserModule.ResetToken() + location.reload() // To prevent bugs from vue-router + }) + } + return Promise.reject(new Error(res.message || 'Error')) + } else { + return response.data + } + }, + (error) => { + Message({ + message: error.message, + type: 'error', + duration: 5 * 1000 + }) + return Promise.reject(error) + } +) + +export default service diff --git a/website/frontend/src/utils/validate.ts b/website/frontend/src/utils/validate.ts new file mode 100644 index 0000000..61fd356 --- /dev/null +++ b/website/frontend/src/utils/validate.ts @@ -0,0 +1,3 @@ +export const isValidUsername = (str: string) => ['admin', 'editor'].indexOf(str.trim()) >= 0 + +export const isExternal = (path: string) => /^(https?:|mailto:|tel:)/.test(path) diff --git a/website/frontend/src/views/404.vue b/website/frontend/src/views/404.vue new file mode 100644 index 0000000..27bd4da --- /dev/null +++ b/website/frontend/src/views/404.vue @@ -0,0 +1,283 @@ + + + + + diff --git a/website/frontend/src/views/dashboard/index.vue b/website/frontend/src/views/dashboard/index.vue new file mode 100644 index 0000000..04d01f8 --- /dev/null +++ b/website/frontend/src/views/dashboard/index.vue @@ -0,0 +1,117 @@ + + + + diff --git a/website/frontend/src/views/debug/index.vue b/website/frontend/src/views/debug/index.vue new file mode 100644 index 0000000..6e851a2 --- /dev/null +++ b/website/frontend/src/views/debug/index.vue @@ -0,0 +1,203 @@ + + + diff --git a/website/frontend/src/views/flamegraph/flamegraph.js b/website/frontend/src/views/flamegraph/flamegraph.js new file mode 100644 index 0000000..2156450 --- /dev/null +++ b/website/frontend/src/views/flamegraph/flamegraph.js @@ -0,0 +1,718 @@ +/* eslint-disable */ +'use strict'; + +let searching = 0; +let zoomclick = null; + +// 以下是公共方法 +function find_child(parent, name, attr) { + let children = parent.childNodes; + for (let i = 0; i < children.length; i++) { + if (children[i].tagName == name) + return (attr != undefined) ? children[i].attributes[attr].value : children[i]; + } + return; +} + +function orig_load(e, attr) { + if (e.attributes["_orig_" + attr] == undefined) return; + e.attributes[attr].value = e.attributes["_orig_" + attr].value; + e.removeAttribute("_orig_" + attr); +} + +function orig_save(e, attr, val) { + if (e.attributes["_orig_" + attr] != undefined) return; + if (e.attributes[attr] == undefined) return; + if (val == undefined) val = e.attributes[attr].value; + e.setAttribute("_orig_" + attr, val); +} + +function g_to_text(e) { + var text = find_child(e, "title").firstChild.nodeValue; + return (text) +} + +function g_to_func(e) { + var func = g_to_text(e); + if (func != null) + func = func.replace(/ .*/, ""); + return (func); +} + +function zoom_child(e, x, ratio) { + if (e.attributes != undefined) { + if (e.attributes["x"] != undefined) { + orig_save(e, "x"); + e.attributes["x"].value = (parseFloat(e.attributes["x"].value) - x - 10) * ratio + 10; + if (e.tagName == "text") e.attributes["x"].value = find_child(e.parentNode, "rect", "x") + 3; + } + if (e.attributes["width"] != undefined) { + orig_save(e, "width"); + const index = e.attributes['node-index'].value; + const width_origin = e.attributes["width"].value; + e.attributes["width"].value = parseFloat(width_origin) * ratio; + e.attributes["width"].value = e.attributes["width"].value; + } + } + + if (e.childNodes == undefined) return; + for (let i = 0, c = e.childNodes; i < c.length; i++) { + zoom_child(c[i], x - 10, ratio); + } +} + +function zoom_parent(e, svg) { + if (e.attributes) { + if (e.attributes["x"] != undefined) { + orig_save(e, "x"); + e.attributes["x"].value = 10; + } + if (e.attributes["width"] != undefined) { + orig_save(e, "width"); + e.attributes["width"].value = parseFloat(svg.width.baseVal.value) - (10 * 2); + } + } + if (e.childNodes == undefined) return; + for (let i = 0, c = e.childNodes; i < c.length; i++) { + zoom_parent(c[i], svg); + } +} + +function update_text(e) { + let r = find_child(e, "rect"); + let t = find_child(e, "text"); + let w = parseFloat(r.attributes["width"].value) - 3; + // let txt = find_child(e, "title").textContent.replace(/\([^(]*\)/, ""); + let txt = find_child(e, "title").textContent.split(' (')[0]; + t.attributes["x"].value = parseFloat(r.attributes["x"].value) + 3; + + // Smaller than this size won't fit anything + if (w < 2 * 12 * 0.59) { + t.textContent = ""; + return; + } + + t.textContent = txt; + // Fit in full text width + if (/^ *$/.test(txt) || t.getSubStringLength(0, txt.length) < w) + return; + + for (let x = txt.length - 2; x > 0; x--) { + if (t.getSubStringLength(0, x + 2) <= w) { + t.textContent = txt.substring(0, x) + ".."; + return; + } + } + t.textContent = ""; +} + +function zoom_reset(e) { + if (e.attributes != undefined) { + orig_load(e, "x"); + orig_load(e, "width"); + } + if (e.childNodes == undefined) return; + for (let i = 0, c = e.childNodes; i < c.length; i++) { + zoom_reset(c[i]); + } +} + +function search(term, matchedtxt, searchbtn) { + let re = new RegExp(term); + let el = document.getElementsByTagName("g"); + let matches = new Object(); + let maxwidth = 0; + for (let i = 0; i < el.length; i++) { + let e = el[i]; + if (e.attributes["class"].value != "func_g") + continue; + let func = g_to_func(e); + let rect = find_child(e, "rect"); + if (rect == null) { + // the rect might be wrapped in an anchor + // if nameattr href is being used + if (rect = find_child(e, "a")) { + rect = find_child(r, "rect"); + } + } + if (func == null || rect == null) + continue; + + // Save max width. Only works as we have a root frame + let w = parseFloat(rect.attributes["width"].value); + if (w > maxwidth) + maxwidth = w; + + if (func.match(re)) { + // highlight + let x = parseFloat(rect.attributes["x"].value); + orig_save(rect, "fill"); + rect.attributes["fill"].value = + "rgb(230,0,230)"; + + // remember matches + if (matches[x] == undefined) { + matches[x] = w; + } else { + if (w > matches[x]) { + // overwrite with parent + matches[x] = w; + } + } + searching = 1; + } + } + if (!searching) { + matchedtxt.style["opacity"] = "1.0"; + matchedtxt.firstChild.nodeValue = "Matched: none"; + return; + } + + searchbtn.style["opacity"] = "1.0"; + searchbtn.firstChild.nodeValue = "Reset Search" + + // calculate percent matched, excluding vertical overlap + let count = 0; + let lastx = -1; + let lastw = 0; + let keys = Array(); + for (let k in matches) { + if (matches.hasOwnProperty(k)) + keys.push(k); + } + // sort the matched frames by their x location + // ascending, then width descending + keys.sort(function (a, b) { + return a - b; + if (a < b || a > b) + return a - b; + return matches[b] - matches[a]; + }); + // Step through frames saving only the biggest bottom-up frames + // thanks to the sort order. This relies on the tree property + // where children are always smaller than their parents. + for (let k in keys) { + let x = parseFloat(keys[k]); + let w = matches[keys[k]]; + if (x >= lastx + lastw) { + count += w; + lastx = x; + lastw = w; + } + } + // display matched percent + matchedtxt.style["opacity"] = "1.0"; + let pct = 100 * count / maxwidth; + if (pct == 100) + pct = "100" + else + pct = pct.toFixed(1) + matchedtxt.firstChild.nodeValue = "Matched: " + pct + "%"; +} + +function reset_search() { + var el = document.getElementsByTagName("rect"); + for (var i = 0; i < el.length; i++) { + orig_load(el[i], "fill") + } +} + +// flamegraph 颜色 +const colorMap = (function () { + function scalarReverse(s) { + return s.split('').reverse().join('') + } + + function nameHash(name) { + let vector = 0 + let weight = 1 + let max = 1 + let mod = 10 + let ord + + name = name.replace(/.(.*?)`/, '') + let splits = name.split('') + for (let i = 0; i < splits.length; i++) { + ord = splits[i].charCodeAt(0) % mod + vector += (ord / (mod++ - 1)) * weight + max += weight + weight *= 0.70 + if (mod > 12) break + } + + return (1 - vector / max) + } + + function color(type, hash, name) { + let v1, v2, v3, r, g, b + if (!type) return 'rgb(0, 0, 0)' + + if (hash) { + v1 = nameHash(name) + v2 = v3 = nameHash(scalarReverse(name)) + } else { + v1 = Math.random() + 1 + v2 = Math.random() + 1 + v3 = Math.random() + 1 + } + + switch (type) { + case 'hot': + r = 205 + Math.round(50 * v3); + g = 0 + Math.round(230 * v1); + b = 0 + Math.round(55 * v2); + return `rgb(${r}, ${g}, ${b})`; + case 'mem': + r = 0 + g = 190 + Math.round(50 * v2) + b = 0 + Math.round(210 * v1) + return `rgb(${r}, ${g}, ${b})` + case 'io': + r = 80 + Math.round(60 * v1) + g = r + b = 190 + Math.round(55 * v2) + return `rgb(${r}, ${g}, ${b})` + default: + throw new Error('Unknown type ' + type) + } + } + + function colorMap(paletteMap, colorTheme, hash, func) { + if (paletteMap[func]) return paletteMap[func] + paletteMap[func] = color(colorTheme, hash, func) + return paletteMap[func] + } + + return colorMap; +})(); + +// 获取渲染的 context 信息 +const contextify = (function () { + + function oneDecimal(x) { + return (Math.round(x * 10) / 10) + } + + function htmlEscape(s) { + return s + .replace(/&/g, '&') + .replace(//g, '>') + } + + function contextify(parsed, opts) { + let each = parsed.each; + let time = parsed.time + let timeMax = opts.timemax + let ypadTop = opts.fontsize * 4 // pad top, include title + let ypadBottom = opts.fontsize * 2 + 10 // pad bottom, include labels + let xpad = 10 + let xpad2 = opts.imagewidth * 0.82 + let xpad3 = opts.imagewidth * 0.92 + let depthMax = 0 + let frameHeight = opts.frameheight + let paletteMap = {} + + if (timeMax < time && timeMax / time > 0.02) { + console.error('Specified timemax %d is less than actual total %d, so it will be ignored', timeMax, time) + timeMax = Infinity + } + + timeMax = Math.min(time, timeMax) + + let widthPerTime = (opts.imagewidth - 2 * xpad) / timeMax + let minWidthTime = opts.minwidth / widthPerTime + + function markNarrowBlocks(nodes) { + function mark(k) { + let val = parsed.nodes[k] + if (typeof val.stime !== 'number') throw new Error('Missing start for ' + k) + if ((val.etime - val.stime) < minWidthTime) { + val.narrow = true + return + } + + val.narrow = false + depthMax = Math.max(val.depth, depthMax) + } + + Object.keys(nodes).forEach(mark) + } + + // NodeProcessor proto + function processNode(node, index) { + let func = node.func + let depth = node.depth + let etime = node.etime + let stime = node.stime + let factor = opts.factor + let countName = opts.countname + let isRoot = !func.length && depth === 0 + + if (isRoot) etime = timeMax + + let samples = Math.round((etime - stime * factor) * 10) / 10 + let costTime = samples * each + let samplesTxt = samples.toLocaleString() + let pct + let pctTxt + let escapedFunc + let name + let sampleInfo + + if (isRoot) { + name = 'all' + sampleInfo = `(${samplesTxt} ${countName} ${utils.formatTime(costTime)}, 100%)` + } else { + pct = Math.round((100 * samples) / (timeMax * factor) * 10) / 10 + pctTxt = pct.toLocaleString() + escapedFunc = htmlEscape(func) + + name = escapedFunc + sampleInfo = `(${samplesTxt} ${countName} ${utils.formatTime(costTime)}), ${pctTxt}%)` + } + if (opts.profile.get_sample) { + sampleInfo = opts.profile.get_sample(node, opts, timeMax) + } + + let x1 = oneDecimal(xpad + stime * widthPerTime) + let x2 = oneDecimal(xpad + etime * widthPerTime) + let y1 = oneDecimal(imageHeight - ypadBottom - (depth + 1) * frameHeight + 1) + let y2 = oneDecimal(imageHeight - ypadBottom - depth * frameHeight) + + let chars = (x2 - x1) / (opts.fontsize * opts.fontwidth) + let showText = false + let text + + if (chars >= 3) { // enough room to display function name? + showText = true + text = func.slice(0, chars) + if (chars < func.length) text = text.slice(0, chars - 2) + '..' + text = htmlEscape(text) + } + + let rect_w = x2 - x1; + let rect_h = y2 - y1; + + return { + name: name + , index: index + , search: name.toLowerCase() + , samples: sampleInfo + , rect_x: x1 + , rect_y: y1 + , rect_w: rect_w + , rect_h: rect_h + , rect_fill: colorMap(paletteMap, opts.colors, opts.hash, func) + , text: text + , text_x: x1 + (showText ? 3 : 0) + , text_y: 3 + (y1 + y2) / 2 + , narrow: node.narrow + , func: htmlEscape(func) + } + } + + function processNodes(nodes) { + let keys = Object.keys(nodes) + let acc = new Array(keys.length) + + for (let i = 0; i < keys.length; i++) { + acc[i] = processNode(nodes[keys[i]], i) + } + + return acc + } + + markNarrowBlocks(parsed.nodes) + + let imageHeight = (depthMax * frameHeight) + ypadTop + ypadBottom + let ctx = Object.assign({}, opts, { + imageheight: imageHeight + , xpad: xpad + , xpad2: xpad2 + , xpad3: xpad3 + , titleX: opts.imagewidth / 2 + , detailsY: imageHeight - (frameHeight / 2) + , viewbox: `0 0 ${opts.imagewidth} ${imageHeight}` + }) + + ctx.nodes = processNodes(parsed.nodes) + return ctx + } + + return contextify; +})(); + +const utils = (function () { + + function formatTime(ts) { + ts = !isNaN(ts) && ts || 0; + ts = ts / 1000; + let str = ''; + if (ts < 1e3) { + str = `${ts.toFixed(2)} ms`; + } else if (ts < 1e3 * 60) { + str = `${(ts / 1e3).toFixed(2)} s`; + } else if (ts < 1e3 * 60 * 60) { + str = `${(ts / (1e3 * 60)).toFixed(2)} min`; + } else if (ts < 1e3 * 60 * 60 * 60) { + str = `${(ts / (1e3 * 60 * 60)).toFixed(2)} h`; + } else { + str = `${ts.toFixed(2)} ms`; + } + + return str; + } + return { formatTime }; +})(); + +function narrowify(context, opts) { + function processNode(n) { + n.class = n.narrow ? 'hidden' : '' + } + + function filterNode(n) { + return !n.narrow + } + + if (opts.removenarrows) context.nodes = context.nodes.filter(filterNode) + else context.nodes.forEach(processNode) +} + + +function _normalize_nodes(node, p, ret) { + ret.push({ + func: node.name, + depth: p.depth, + stime: p.stime, + etime: p.stime + p.fconfig.profile.get_value(node), + }) + if (!node.children) { + return ret; + } + node.children.sort(function (a, b) { + return a.rettime < b.rettime + }) + let tmptime = 0; + for (let i = 0; i < node.children.length; i++) { + let children = node.children[i]; + _normalize_nodes(children, {fconfig: p.fconfig, stime: p.stime + tmptime, depth: p.depth + 1}, ret) + tmptime += p.fconfig.profile.get_value(children); + } + return ret +} +function normalize_nodes(data, ret) { + let fconfig = data.fconfig; + let nodes = data.data; + + return [fconfig.profile.total, _normalize_nodes(nodes, {fconfig: fconfig, stime: 0, depth: 0}, ret)] +} + +/** + * @component: views/common/profiler/flamegraph.vue + * @vue-data: mounted + * @descript: 计算 svg 渲染数据 + */ +function mounted() { + // 计算 svg 真实宽度 + const imagewidth = this.$refs.svg.clientWidth; + this.flamegraphData.fconfig.imagewidth = imagewidth; + // 传输过程中 timemax 会丢失 + this.flamegraphData.fconfig.timemax = Infinity; + let normalize = normalize_nodes(this.flamegraphData, []); + this.flamegraphData.parsed = {each: 1, time: normalize[0], nodes: normalize[1]}; + // 计算 svg 其余渲染数据 + const context = contextify(this.flamegraphData.parsed, this.flamegraphData.fconfig); + narrowify(context, this.flamegraphData.fconfig); + + this.data = context; +} + +/** + * @component: views/common/profiler/flamegraph.vue + * @vue-data: methods + * @descript: 展示函数详情 + */ +function s(info) { + this.show = 'Function: ' + info; +} + +/** + * @component: views/common/profiler/flamegraph.vue + * @vue-data: methods + * @descript: 展示默认值 + */ +function c() { + this.show = 'SWT'; +} + +/** + * @component: views/common/profiler/flamegraph.vue + * @vue-data: methods + * @descript: 放大点击处 + */ +function zoom(event) { + let node = event.currentTarget; + let index = node.attributes['node-index'].value; + zoomclick = index; + if (this.need_unzoom[index]) { + unzoom.call(this); + } + //let svg = document.getElementsByTagName("svg")[0]; + let svg = this.$refs.svg; + let attr = find_child(node, "rect").attributes; + let width = parseFloat(attr["width"].value); + let xmin = parseFloat(attr["x"].value); + let xmax = parseFloat(xmin + width); + let ymin = parseFloat(attr["y"].value); + // 计算倍率使用原来的 width + let ratio = (svg.width.baseVal.value - 2 * 10) / parseFloat(attr["width"].value); + let fudge = 0.0001; + + let unzoombtn = this.$refs.unzoom; + unzoombtn.style["opacity"] = "1.0"; + + let el = document.getElementsByTagName("g"); + let upstack; + for (let i = 0; i < el.length; i++) { + let e = el[i]; + let ei = e.attributes['node-index'].value; + let a = find_child(e, "rect").attributes; + let ex = parseFloat(a["x"].value); + let ew = parseFloat(a["width"].value); + if (0 == 0) { + upstack = parseFloat(a["y"].value) > ymin; + } else { + upstack = parseFloat(a["y"].value) < ymin; + } + if (upstack) { + if (ex <= xmin && (ex + ew + fudge) >= xmax) { + e.style["opacity"] = "0.5"; + zoom_parent(e, svg); + this.need_unzoom[e.attributes['node-index'].value] = true; + update_text(e); + } + else + e.style["display"] = "none"; + } + else { + if (ex < xmin || ex + fudge >= xmax) { + e.style["display"] = "none"; + } + else { + zoom_child(e, xmin, ratio); + this.need_unzoom[e.attributes['node-index'].value] = false; + update_text(e); + } + } + } +} + +/** + * @component: views/common/profiler/flamegraph.vue + * @vue-data: methods + * @descript: 缩小点击 + */ +function unzoom() { + let unzoombtn = this.$refs.unzoom; + unzoombtn.style["opacity"] = "0.0"; + + let el = document.getElementsByTagName("g"); + for (let i = 0; i < el.length; i++) { + el[i].style["display"] = "block"; + el[i].style["opacity"] = "1"; + zoom_reset(el[i]); + update_text(el[i]); + } +} + +/** + * @component: views/common/profiler/flamegraph.vue + * @vue-data: methods + * @descript: 鼠标置于搜索框上 + */ +function searchover(e) { + this.$refs.search.style["opacity"] = "1.0"; +} + +/** + * @component: views/common/profiler/flamegraph.vue + * @vue-data: methods + * @descript: 鼠标移开搜索框 + */ +function searchout(e) { + let searchbtn = this.$refs.search; + if (searching) { + searchbtn.style["opacity"] = "1.0"; + } else { + searchbtn.style["opacity"] = "0.1"; + } +} + +/** + * @component: views/common/profiler/flamegraph.vue + * @vue-data: search_prompt + * @descript: 开始搜索 + */ +function search_prompt() { + let searchbtn = this.$refs.search; + let matchedtxt = this.$refs.matched; + if (!searching) { + let term = prompt("请输入需要查询的函数名: (允许输入正则表达式,例如: ^ext4_)", ""); + if (term != null) { + search(term, matchedtxt, searchbtn); + } + } else { + reset_search(); + searching = 0; + searchbtn.style["opacity"] = "0.1"; + searchbtn.firstChild.nodeValue = "Search" + matchedtxt.style["opacity"] = "0.0"; + matchedtxt.firstChild.nodeValue = ""; + } +} + +function set_parent_inf() { + this.showBigPic + ? this.$emit('hidePic') + : this.$emit('changPic'); + +} + +/** + * @component: views/common/profiler/flamegraph.vue + * @vue-data: computed + * @descript: 函数节点展示 + */ +function nodes() { + return this.data.nodes || []; +} + +/** + * @component: views/common/profiler/flamegraph.vue + * @vue-data: watch + * @descript: 缩放时进行处理 + */ +function showBigPic() { + // 计算 svg 真实宽度 + const imagewidth = this.$refs.svg.clientWidth; + this.flamegraphData.fconfig.imagewidth = imagewidth; + // 传输过程中 timemax 会丢失 + this.flamegraphData.fconfig.timemax = Infinity; + let normalize = normalize_nodes(this.flamegraphData, []); + this.flamegraphData.parsed = {each: 1, time: normalize[0], nodes: normalize[1]}; + // 计算 svg 其余渲染数据 + const context = contextify(this.flamegraphData.parsed, this.flamegraphData.fconfig); + narrowify(context, this.flamegraphData.fconfig); + this.data = context; + + unzoom.call(this); +} + +//导出 flamegraph.vue 所需 +export default { + mounted, + methods: { s, c, zoom, unzoom, searchover, searchout, search_prompt, set_parent_inf }, + computed: { nodes }, + watch: { showBigPic } +} diff --git a/website/frontend/src/views/flamegraph/flamegraph.vue b/website/frontend/src/views/flamegraph/flamegraph.vue new file mode 100644 index 0000000..f498f68 --- /dev/null +++ b/website/frontend/src/views/flamegraph/flamegraph.vue @@ -0,0 +1,71 @@ + + + diff --git a/website/frontend/src/views/form/index.vue b/website/frontend/src/views/form/index.vue new file mode 100644 index 0000000..2dcc2ef --- /dev/null +++ b/website/frontend/src/views/form/index.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/website/frontend/src/views/login/index.vue b/website/frontend/src/views/login/index.vue new file mode 100644 index 0000000..209c257 --- /dev/null +++ b/website/frontend/src/views/login/index.vue @@ -0,0 +1,271 @@ + + + + + + + diff --git a/website/frontend/src/views/nested/menu1/index.vue b/website/frontend/src/views/nested/menu1/index.vue new file mode 100644 index 0000000..64d4f7c --- /dev/null +++ b/website/frontend/src/views/nested/menu1/index.vue @@ -0,0 +1,24 @@ + + + diff --git a/website/frontend/src/views/nested/menu1/menu1-1/index.vue b/website/frontend/src/views/nested/menu1/menu1-1/index.vue new file mode 100644 index 0000000..d81d26c --- /dev/null +++ b/website/frontend/src/views/nested/menu1/menu1-1/index.vue @@ -0,0 +1,20 @@ + + + diff --git a/website/frontend/src/views/nested/menu1/menu1-2/index.vue b/website/frontend/src/views/nested/menu1/menu1-2/index.vue new file mode 100644 index 0000000..fbd013a --- /dev/null +++ b/website/frontend/src/views/nested/menu1/menu1-2/index.vue @@ -0,0 +1,25 @@ + + + diff --git a/website/frontend/src/views/nested/menu1/menu1-2/menu1-2-1/index.vue b/website/frontend/src/views/nested/menu1/menu1-2/menu1-2-1/index.vue new file mode 100644 index 0000000..b2913ca --- /dev/null +++ b/website/frontend/src/views/nested/menu1/menu1-2/menu1-2-1/index.vue @@ -0,0 +1,18 @@ + + + diff --git a/website/frontend/src/views/nested/menu1/menu1-2/menu1-2-2/index.vue b/website/frontend/src/views/nested/menu1/menu1-2/menu1-2-2/index.vue new file mode 100644 index 0000000..cf3fde9 --- /dev/null +++ b/website/frontend/src/views/nested/menu1/menu1-2/menu1-2-2/index.vue @@ -0,0 +1,18 @@ + + + diff --git a/website/frontend/src/views/nested/menu1/menu1-3/index.vue b/website/frontend/src/views/nested/menu1/menu1-3/index.vue new file mode 100644 index 0000000..c488c9d --- /dev/null +++ b/website/frontend/src/views/nested/menu1/menu1-3/index.vue @@ -0,0 +1,18 @@ + + + diff --git a/website/frontend/src/views/nested/menu2/index.vue b/website/frontend/src/views/nested/menu2/index.vue new file mode 100644 index 0000000..42161fb --- /dev/null +++ b/website/frontend/src/views/nested/menu2/index.vue @@ -0,0 +1,17 @@ + + + diff --git a/website/frontend/src/views/nodeFilter.vue b/website/frontend/src/views/nodeFilter.vue new file mode 100644 index 0000000..38a5daa --- /dev/null +++ b/website/frontend/src/views/nodeFilter.vue @@ -0,0 +1,117 @@ + + + diff --git a/website/frontend/src/views/profiler/index.vue b/website/frontend/src/views/profiler/index.vue new file mode 100644 index 0000000..9aa7e4f --- /dev/null +++ b/website/frontend/src/views/profiler/index.vue @@ -0,0 +1,350 @@ + + + diff --git a/website/frontend/src/views/table/index.vue b/website/frontend/src/views/table/index.vue new file mode 100644 index 0000000..513391f --- /dev/null +++ b/website/frontend/src/views/table/index.vue @@ -0,0 +1,113 @@ + + + diff --git a/website/frontend/src/views/tree/index.vue b/website/frontend/src/views/tree/index.vue new file mode 100644 index 0000000..d3fba1a --- /dev/null +++ b/website/frontend/src/views/tree/index.vue @@ -0,0 +1,79 @@ + + + diff --git a/website/frontend/tests/unit/.eslintrc.js b/website/frontend/tests/unit/.eslintrc.js new file mode 100644 index 0000000..958d51b --- /dev/null +++ b/website/frontend/tests/unit/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + env: { + jest: true + } +} diff --git a/website/frontend/tests/unit/components/Breadcrumb.spec.ts b/website/frontend/tests/unit/components/Breadcrumb.spec.ts new file mode 100644 index 0000000..3db72be --- /dev/null +++ b/website/frontend/tests/unit/components/Breadcrumb.spec.ts @@ -0,0 +1,97 @@ +import { mount, createLocalVue } from '@vue/test-utils' +import VueRouter from 'vue-router' +import ElementUI from 'element-ui' +import Breadcrumb from '@/components/Breadcrumb/index.vue' + +const localVue = createLocalVue() +localVue.use(VueRouter) +localVue.use(ElementUI) + +const routes = [ + { + path: '/', + children: [{ + path: 'dashboard' + }] + }, + { + path: '/menu', + children: [{ + path: 'menu1', + meta: { title: 'menu1' }, + children: [{ + path: 'menu1-1', + meta: { title: 'menu1-1' } + }, + { + path: 'menu1-2', + redirect: 'noredirect', + meta: { title: 'menu1-2' }, + children: [{ + path: 'menu1-2-1', + meta: { title: 'menu1-2-1' } + }, + { + path: 'menu1-2-2' + }] + }] + }] + }] + +const router = new VueRouter({ + mode: 'hash', // 'history', + routes +}) + +describe('Breadcrumb.vue', () => { + const wrapper = mount(Breadcrumb, { + localVue, + router + }) + + it('dashboard', () => { + router.push('/dashboard') + const len = wrapper.findAll('.el-breadcrumb__inner').length + expect(len).toBe(1) + }) + + it('normal route', () => { + router.push('/menu/menu1') + const len = wrapper.findAll('.el-breadcrumb__inner').length + expect(len).toBe(2) + }) + + it('nested route', () => { + router.push('/menu/menu1/menu1-2/menu1-2-1') + const len = wrapper.findAll('.el-breadcrumb__inner').length + expect(len).toBe(4) + }) + + it('no meta.title', () => { + router.push('/menu/menu1/menu1-2/menu1-2-2') + const len = wrapper.findAll('.el-breadcrumb__inner').length + expect(len).toBe(3) + }) + + it('click link', () => { + router.push('/menu/menu1/menu1-2/menu1-2-2') + const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner') + const second = breadcrumbArray.at(1) + const href = second.find('a').text() + expect(href).toBe('menu1') + }) + + it('noredirect', () => { + router.push('/menu/menu1/menu1-2/menu1-2-1') + const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner') + const redirectBreadcrumb = breadcrumbArray.at(2) + expect(redirectBreadcrumb.contains('a')).toBe(false) + }) + + it('last breadcrumb', () => { + router.push('/menu/menu1/menu1-2/menu1-2-1') + const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner') + const redirectBreadcrumb = breadcrumbArray.at(3) + expect(redirectBreadcrumb.contains('a')).toBe(false) + }) +}) diff --git a/website/frontend/tests/unit/utils/validate.spec.ts b/website/frontend/tests/unit/utils/validate.spec.ts new file mode 100644 index 0000000..5403c95 --- /dev/null +++ b/website/frontend/tests/unit/utils/validate.spec.ts @@ -0,0 +1,15 @@ +import { isValidUsername, isExternal } from '@/utils/validate' + +describe('Utils:validate', () => { + it('isValidUsername', () => { + expect(isValidUsername('admin')).toBe(true) + expect(isValidUsername('editor')).toBe(true) + expect(isValidUsername('xxxx')).toBe(false) + }) + + it('isExternal', () => { + expect(isExternal('https://www.armour.com/')).toBe(true) + expect(isExternal('mailto:someone@test.com')).toBe(true) + expect(isExternal('123aBC')).toBe(false) + }) +}) diff --git a/website/frontend/tsconfig.json b/website/frontend/tsconfig.json new file mode 100644 index 0000000..d350cdc --- /dev/null +++ b/website/frontend/tsconfig.json @@ -0,0 +1,41 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "strict": true, + "jsx": "preserve", + "importHelpers": true, + "moduleResolution": "node", + "experimentalDecorators": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "sourceMap": false, + "baseUrl": ".", + "types": [ + "node", + "jest", + "webpack-env" + ], + "paths": { + "@/*": [ + "src/*" + ] + }, + "lib": [ + "esnext", + "dom", + "dom.iterable", + "scripthost" + ] + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx", + "src/**/*.vue", + "tests/**/*.ts", + "tests/**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/website/frontend/vue.config.js b/website/frontend/vue.config.js new file mode 100644 index 0000000..042157a --- /dev/null +++ b/website/frontend/vue.config.js @@ -0,0 +1,32 @@ +const path = require('path') +const name = 'SWT Admin' + +// TODO: Remember to change this to fit your need +// NODE_ENV = 'production' +// VUE_APP_BASE_API = 'http://10.0.0.142:9518/' + +// NODE_ENV = 'development' +// VUE_APP_BASE_API = 'http://127.0.0.1:9527/' + +module.exports = { + outputDir: "../backend/www/admin", + publicPath: process.env.NODE_ENV === 'production' ? '/admin/' : '/', + lintOnSave: process.env.NODE_ENV === 'development', + pwa: { + name: name + }, + pluginOptions: { + 'style-resources-loader': { + preProcessor: 'scss', + patterns: [ + path.resolve(__dirname, 'src/styles/_variables.scss'), + path.resolve(__dirname, 'src/styles/_mixins.scss') + ] + } + }, + chainWebpack(config) { + // Provide the app's title in webpack's name field, so that + // it can be accessed in index.html to inject the correct title. + config.set('name', name) + } +} diff --git a/website/frontend/yarn.lock b/website/frontend/yarn.lock new file mode 100644 index 0000000..175dc81 --- /dev/null +++ b/website/frontend/yarn.lock @@ -0,0 +1,12226 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.0.0-beta.35", "@babel/code-frame@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" + integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== + dependencies: + "@babel/highlight" "^7.0.0" + +"@babel/core@^7.0.0", "@babel/core@^7.1.0": + version "7.7.7" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.7.7.tgz#ee155d2e12300bcc0cff6a8ad46f2af5063803e9" + integrity sha512-jlSjuj/7z138NLZALxVgrx13AOtqip42ATZP7+kYl53GvDV6+4dCek1mVUo8z8c8Xnw/mx2q3d9HWh3griuesQ== + dependencies: + "@babel/code-frame" "^7.5.5" + "@babel/generator" "^7.7.7" + "@babel/helpers" "^7.7.4" + "@babel/parser" "^7.7.7" + "@babel/template" "^7.7.4" + "@babel/traverse" "^7.7.4" + "@babel/types" "^7.7.4" + convert-source-map "^1.7.0" + debug "^4.1.0" + json5 "^2.1.0" + lodash "^4.17.13" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.4.0", "@babel/generator@^7.7.4", "@babel/generator@^7.7.7": + version "7.7.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.7.7.tgz#859ac733c44c74148e1a72980a64ec84b85f4f45" + integrity sha512-/AOIBpHh/JU1l0ZFS4kiRCBnLi6OTHzh0RPk3h9isBxkkqELtQNFi1Vr/tiG9p1yfoUdKVwISuXWQR+hwwM4VQ== + dependencies: + "@babel/types" "^7.7.4" + jsesc "^2.5.1" + lodash "^4.17.13" + source-map "^0.5.0" + +"@babel/helper-annotate-as-pure@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.7.4.tgz#bb3faf1e74b74bd547e867e48f551fa6b098b6ce" + integrity sha512-2BQmQgECKzYKFPpiycoF9tlb5HA4lrVyAmLLVK177EcQAqjVLciUb2/R+n1boQ9y5ENV3uz2ZqiNw7QMBBw1Og== + dependencies: + "@babel/types" "^7.7.4" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.7.4.tgz#5f73f2b28580e224b5b9bd03146a4015d6217f5f" + integrity sha512-Biq/d/WtvfftWZ9Uf39hbPBYDUo986m5Bb4zhkeYDGUllF43D+nUe5M6Vuo6/8JDK/0YX/uBdeoQpyaNhNugZQ== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.7.4" + "@babel/types" "^7.7.4" + +"@babel/helper-call-delegate@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.7.4.tgz#621b83e596722b50c0066f9dc37d3232e461b801" + integrity sha512-8JH9/B7J7tCYJ2PpWVpw9JhPuEVHztagNVuQAFBVFYluRMlpG7F1CgKEgGeL6KFqcsIa92ZYVj6DSc0XwmN1ZA== + dependencies: + "@babel/helper-hoist-variables" "^7.7.4" + "@babel/traverse" "^7.7.4" + "@babel/types" "^7.7.4" + +"@babel/helper-create-class-features-plugin@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.7.4.tgz#fce60939fd50618610942320a8d951b3b639da2d" + integrity sha512-l+OnKACG4uiDHQ/aJT8dwpR+LhCJALxL0mJ6nzjB25e5IPwqV1VOsY7ah6UB1DG+VOXAIMtuC54rFJGiHkxjgA== + dependencies: + "@babel/helper-function-name" "^7.7.4" + "@babel/helper-member-expression-to-functions" "^7.7.4" + "@babel/helper-optimise-call-expression" "^7.7.4" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-replace-supers" "^7.7.4" + "@babel/helper-split-export-declaration" "^7.7.4" + +"@babel/helper-create-regexp-features-plugin@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.7.4.tgz#6d5762359fd34f4da1500e4cff9955b5299aaf59" + integrity sha512-Mt+jBKaxL0zfOIWrfQpnfYCN7/rS6GKx6CCCfuoqVVd+17R8zNDlzVYmIi9qyb2wOk002NsmSTDymkIygDUH7A== + dependencies: + "@babel/helper-regex" "^7.4.4" + regexpu-core "^4.6.0" + +"@babel/helper-define-map@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.7.4.tgz#2841bf92eb8bd9c906851546fe6b9d45e162f176" + integrity sha512-v5LorqOa0nVQUvAUTUF3KPastvUt/HzByXNamKQ6RdJRTV7j8rLL+WB5C/MzzWAwOomxDhYFb1wLLxHqox86lg== + dependencies: + "@babel/helper-function-name" "^7.7.4" + "@babel/types" "^7.7.4" + lodash "^4.17.13" + +"@babel/helper-explode-assignable-expression@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.7.4.tgz#fa700878e008d85dc51ba43e9fb835cddfe05c84" + integrity sha512-2/SicuFrNSXsZNBxe5UGdLr+HZg+raWBLE9vC98bdYOKX/U6PY0mdGlYUJdtTDPSU0Lw0PNbKKDpwYHJLn2jLg== + dependencies: + "@babel/traverse" "^7.7.4" + "@babel/types" "^7.7.4" + +"@babel/helper-function-name@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz#ab6e041e7135d436d8f0a3eca15de5b67a341a2e" + integrity sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ== + dependencies: + "@babel/helper-get-function-arity" "^7.7.4" + "@babel/template" "^7.7.4" + "@babel/types" "^7.7.4" + +"@babel/helper-get-function-arity@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz#cb46348d2f8808e632f0ab048172130e636005f0" + integrity sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA== + dependencies: + "@babel/types" "^7.7.4" + +"@babel/helper-hoist-variables@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.7.4.tgz#612384e3d823fdfaaf9fce31550fe5d4db0f3d12" + integrity sha512-wQC4xyvc1Jo/FnLirL6CEgPgPCa8M74tOdjWpRhQYapz5JC7u3NYU1zCVoVAGCE3EaIP9T1A3iW0WLJ+reZlpQ== + dependencies: + "@babel/types" "^7.7.4" + +"@babel/helper-member-expression-to-functions@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.7.4.tgz#356438e2569df7321a8326644d4b790d2122cb74" + integrity sha512-9KcA1X2E3OjXl/ykfMMInBK+uVdfIVakVe7W7Lg3wfXUNyS3Q1HWLFRwZIjhqiCGbslummPDnmb7vIekS0C1vw== + dependencies: + "@babel/types" "^7.7.4" + +"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.7.4.tgz#e5a92529f8888bf319a6376abfbd1cebc491ad91" + integrity sha512-dGcrX6K9l8258WFjyDLJwuVKxR4XZfU0/vTUgOQYWEnRD8mgr+p4d6fCUMq/ys0h4CCt/S5JhbvtyErjWouAUQ== + dependencies: + "@babel/types" "^7.7.4" + +"@babel/helper-module-transforms@^7.7.4", "@babel/helper-module-transforms@^7.7.5": + version "7.7.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.7.5.tgz#d044da7ffd91ec967db25cd6748f704b6b244835" + integrity sha512-A7pSxyJf1gN5qXVcidwLWydjftUN878VkalhXX5iQDuGyiGK3sOrrKKHF4/A4fwHtnsotv/NipwAeLzY4KQPvw== + dependencies: + "@babel/helper-module-imports" "^7.7.4" + "@babel/helper-simple-access" "^7.7.4" + "@babel/helper-split-export-declaration" "^7.7.4" + "@babel/template" "^7.7.4" + "@babel/types" "^7.7.4" + lodash "^4.17.13" + +"@babel/helper-optimise-call-expression@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.7.4.tgz#034af31370d2995242aa4df402c3b7794b2dcdf2" + integrity sha512-VB7gWZ2fDkSuqW6b1AKXkJWO5NyNI3bFL/kK79/30moK57blr6NbH8xcl2XcKCwOmJosftWunZqfO84IGq3ZZg== + dependencies: + "@babel/types" "^7.7.4" + +"@babel/helper-plugin-utils@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" + integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== + +"@babel/helper-regex@^7.0.0", "@babel/helper-regex@^7.4.4": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.5.5.tgz#0aa6824f7100a2e0e89c1527c23936c152cab351" + integrity sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw== + dependencies: + lodash "^4.17.13" + +"@babel/helper-remap-async-to-generator@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.7.4.tgz#c68c2407350d9af0e061ed6726afb4fff16d0234" + integrity sha512-Sk4xmtVdM9sA/jCI80f+KS+Md+ZHIpjuqmYPk1M7F/upHou5e4ReYmExAiu6PVe65BhJPZA2CY9x9k4BqE5klw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.7.4" + "@babel/helper-wrap-function" "^7.7.4" + "@babel/template" "^7.7.4" + "@babel/traverse" "^7.7.4" + "@babel/types" "^7.7.4" + +"@babel/helper-replace-supers@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.7.4.tgz#3c881a6a6a7571275a72d82e6107126ec9e2cdd2" + integrity sha512-pP0tfgg9hsZWo5ZboYGuBn/bbYT/hdLPVSS4NMmiRJdwWhP0IznPwN9AE1JwyGsjSPLC364I0Qh5p+EPkGPNpg== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.7.4" + "@babel/helper-optimise-call-expression" "^7.7.4" + "@babel/traverse" "^7.7.4" + "@babel/types" "^7.7.4" + +"@babel/helper-simple-access@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.7.4.tgz#a169a0adb1b5f418cfc19f22586b2ebf58a9a294" + integrity sha512-zK7THeEXfan7UlWsG2A6CI/L9jVnI5+xxKZOdej39Y0YtDYKx9raHk5F2EtK9K8DHRTihYwg20ADt9S36GR78A== + dependencies: + "@babel/template" "^7.7.4" + "@babel/types" "^7.7.4" + +"@babel/helper-split-export-declaration@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz#57292af60443c4a3622cf74040ddc28e68336fd8" + integrity sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug== + dependencies: + "@babel/types" "^7.7.4" + +"@babel/helper-wrap-function@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.7.4.tgz#37ab7fed5150e22d9d7266e830072c0cdd8baace" + integrity sha512-VsfzZt6wmsocOaVU0OokwrIytHND55yvyT4BPB9AIIgwr8+x7617hetdJTsuGwygN5RC6mxA9EJztTjuwm2ofg== + dependencies: + "@babel/helper-function-name" "^7.7.4" + "@babel/template" "^7.7.4" + "@babel/traverse" "^7.7.4" + "@babel/types" "^7.7.4" + +"@babel/helpers@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.7.4.tgz#62c215b9e6c712dadc15a9a0dcab76c92a940302" + integrity sha512-ak5NGZGJ6LV85Q1Zc9gn2n+ayXOizryhjSUBTdu5ih1tlVCJeuQENzc4ItyCVhINVXvIT/ZQ4mheGIsfBkpskg== + dependencies: + "@babel/template" "^7.7.4" + "@babel/traverse" "^7.7.4" + "@babel/types" "^7.7.4" + +"@babel/highlight@^7.0.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540" + integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ== + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.7.4", "@babel/parser@^7.7.7": + version "7.7.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.7.tgz#1b886595419cf92d811316d5b715a53ff38b4937" + integrity sha512-WtTZMZAZLbeymhkd/sEaPD8IQyGAhmuTuvTzLiCFM7iXiVdY0gc0IaI+cW0fh1BnSMbJSzXX6/fHllgHKwHhXw== + +"@babel/plugin-proposal-async-generator-functions@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.7.4.tgz#0351c5ac0a9e927845fffd5b82af476947b7ce6d" + integrity sha512-1ypyZvGRXriY/QP668+s8sFr2mqinhkRDMPSQLNghCQE+GAkFtp+wkHVvg2+Hdki8gwP+NFzJBJ/N1BfzCCDEw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-remap-async-to-generator" "^7.7.4" + "@babel/plugin-syntax-async-generators" "^7.7.4" + +"@babel/plugin-proposal-class-properties@^7.0.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.7.4.tgz#2f964f0cb18b948450362742e33e15211e77c2ba" + integrity sha512-EcuXeV4Hv1X3+Q1TsuOmyyxeTRiSqurGJ26+I/FW1WbymmRRapVORm6x1Zl3iDIHyRxEs+VXWp6qnlcfcJSbbw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.7.4" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-proposal-decorators@^7.1.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.7.4.tgz#58c1e21d21ea12f9f5f0a757e46e687b94a7ab2b" + integrity sha512-GftcVDcLCwVdzKmwOBDjATd548+IE+mBo7ttgatqNDR7VG7GqIuZPtRWlMLHbhTXhcnFZiGER8iIYl1n/imtsg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.7.4" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-decorators" "^7.7.4" + +"@babel/plugin-proposal-json-strings@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.7.4.tgz#7700a6bfda771d8dc81973249eac416c6b4c697d" + integrity sha512-wQvt3akcBTfLU/wYoqm/ws7YOAQKu8EVJEvHip/mzkNtjaclQoCCIqKXFP5/eyfnfbQCDV3OLRIK3mIVyXuZlw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-json-strings" "^7.7.4" + +"@babel/plugin-proposal-object-rest-spread@^7.3.4": + version "7.7.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.7.7.tgz#9f27075004ab99be08c5c1bd653a2985813cb370" + integrity sha512-3qp9I8lelgzNedI3hrhkvhaEYree6+WHnyA/q4Dza9z7iEIs1eyhWyJnetk3jJ69RT0AT4G0UhEGwyGFJ7GUuQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-object-rest-spread" "^7.7.4" + +"@babel/plugin-proposal-optional-catch-binding@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.7.4.tgz#ec21e8aeb09ec6711bc0a39ca49520abee1de379" + integrity sha512-DyM7U2bnsQerCQ+sejcTNZh8KQEUuC3ufzdnVnSiUv/qoGJp2Z3hanKL18KDhsBT5Wj6a7CMT5mdyCNJsEaA9w== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.7.4" + +"@babel/plugin-proposal-unicode-property-regex@^7.2.0": + version "7.7.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.7.7.tgz#433fa9dac64f953c12578b29633f456b68831c4e" + integrity sha512-80PbkKyORBUVm1fbTLrHpYdJxMThzM1UqFGh0ALEhO9TYbG86Ah9zQYAB/84axz2vcxefDLdZwWwZNlYARlu9w== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.7.4" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-async-generators@^7.2.0", "@babel/plugin-syntax-async-generators@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.7.4.tgz#331aaf310a10c80c44a66b238b6e49132bd3c889" + integrity sha512-Li4+EjSpBgxcsmeEF8IFcfV/+yJGxHXDirDkEoyFjumuwbmfCVHUt0HuowD/iGM7OhIRyXJH9YXxqiH6N815+g== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-decorators@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.7.4.tgz#3c91cfee2a111663ff3ac21b851140f5a52a4e0b" + integrity sha512-0oNLWNH4k5ZbBVfAwiTU53rKFWIeTh6ZlaWOXWJc4ywxs0tjz5fc3uZ6jKAnZSxN98eXVgg7bJIuzjX+3SXY+A== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-dynamic-import@^7.0.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.7.4.tgz#29ca3b4415abfe4a5ec381e903862ad1a54c3aec" + integrity sha512-jHQW0vbRGvwQNgyVxwDh4yuXu4bH1f5/EICJLAhl1SblLs2CDhrsmCk+v5XLdE9wxtAFRyxx+P//Iw+a5L/tTg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-json-strings@^7.2.0", "@babel/plugin-syntax-json-strings@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.7.4.tgz#86e63f7d2e22f9e27129ac4e83ea989a382e86cc" + integrity sha512-QpGupahTQW1mHRXddMG5srgpHWqRLwJnJZKXTigB9RPFCCGbDGCgBeM/iC82ICXp414WeYx/tD54w7M2qRqTMg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.7.4.tgz#dab2b56a36fb6c3c222a1fbc71f7bf97f327a9ec" + integrity sha512-wuy6fiMe9y7HeZBWXYCGt2RGxZOj0BImZ9EyXJVnVGBKO/Br592rbR3rtIQn0eQhAk9vqaKP5n8tVqEFBQMfLg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.2.0", "@babel/plugin-syntax-object-rest-spread@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.7.4.tgz#47cf220d19d6d0d7b154304701f468fc1cc6ff46" + integrity sha512-mObR+r+KZq0XhRVS2BrBKBpr5jqrqzlPvS9C9vuOf5ilSwzloAl7RPWLrgKdWS6IreaVrjHxTjtyqFiOisaCwg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.2.0", "@babel/plugin-syntax-optional-catch-binding@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.7.4.tgz#a3e38f59f4b6233867b4a92dcb0ee05b2c334aa6" + integrity sha512-4ZSuzWgFxqHRE31Glu+fEr/MirNZOMYmD/0BhBWyLyOOQz/gTAl7QmWm2hX1QxEIXsr2vkdlwxIzTyiYRC4xcQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-arrow-functions@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.7.4.tgz#76309bd578addd8aee3b379d809c802305a98a12" + integrity sha512-zUXy3e8jBNPiffmqkHRNDdZM2r8DWhCB7HhcoyZjiK1TxYEluLHAvQuYnTT+ARqRpabWqy/NHkO6e3MsYB5YfA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-async-to-generator@^7.3.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.7.4.tgz#694cbeae6d613a34ef0292713fa42fb45c4470ba" + integrity sha512-zpUTZphp5nHokuy8yLlyafxCJ0rSlFoSHypTUWgpdwoDXWQcseaect7cJ8Ppk6nunOM6+5rPMkod4OYKPR5MUg== + dependencies: + "@babel/helper-module-imports" "^7.7.4" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-remap-async-to-generator" "^7.7.4" + +"@babel/plugin-transform-block-scoped-functions@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.7.4.tgz#d0d9d5c269c78eaea76227ace214b8d01e4d837b" + integrity sha512-kqtQzwtKcpPclHYjLK//3lH8OFsCDuDJBaFhVwf8kqdnF6MN4l618UDlcA7TfRs3FayrHj+svYnSX8MC9zmUyQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-block-scoping@^7.3.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.7.4.tgz#200aad0dcd6bb80372f94d9e628ea062c58bf224" + integrity sha512-2VBe9u0G+fDt9B5OV5DQH4KBf5DoiNkwFKOz0TCvBWvdAN2rOykCTkrL+jTLxfCAm76l9Qo5OqL7HBOx2dWggg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + lodash "^4.17.13" + +"@babel/plugin-transform-classes@^7.3.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.7.4.tgz#c92c14be0a1399e15df72667067a8f510c9400ec" + integrity sha512-sK1mjWat7K+buWRuImEzjNf68qrKcrddtpQo3swi9j7dUcG6y6R6+Di039QN2bD1dykeswlagupEmpOatFHHUg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.7.4" + "@babel/helper-define-map" "^7.7.4" + "@babel/helper-function-name" "^7.7.4" + "@babel/helper-optimise-call-expression" "^7.7.4" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-replace-supers" "^7.7.4" + "@babel/helper-split-export-declaration" "^7.7.4" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.7.4.tgz#e856c1628d3238ffe12d668eb42559f79a81910d" + integrity sha512-bSNsOsZnlpLLyQew35rl4Fma3yKWqK3ImWMSC/Nc+6nGjC9s5NFWAer1YQ899/6s9HxO2zQC1WoFNfkOqRkqRQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-destructuring@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.7.4.tgz#2b713729e5054a1135097b6a67da1b6fe8789267" + integrity sha512-4jFMXI1Cu2aXbcXXl8Lr6YubCn6Oc7k9lLsu8v61TZh+1jny2BWmdtvY9zSUlLdGUvcy9DMAWyZEOqjsbeg/wA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-dotall-regex@^7.2.0": + version "7.7.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.7.7.tgz#3e9713f1b69f339e87fa796b097d73ded16b937b" + integrity sha512-b4in+YlTeE/QmTgrllnb3bHA0HntYvjz8O3Mcbx75UBPJA2xhb5A8nle498VhxSXJHQefjtQxpnLPehDJ4TRlg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.7.4" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-duplicate-keys@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.7.4.tgz#3d21731a42e3f598a73835299dd0169c3b90ac91" + integrity sha512-g1y4/G6xGWMD85Tlft5XedGaZBCIVN+/P0bs6eabmcPP9egFleMAo65OOjlhcz1njpwagyY3t0nsQC9oTFegJA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-exponentiation-operator@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.7.4.tgz#dd30c0191e3a1ba19bcc7e389bdfddc0729d5db9" + integrity sha512-MCqiLfCKm6KEA1dglf6Uqq1ElDIZwFuzz1WH5mTf8k2uQSxEJMbOIEh7IZv7uichr7PMfi5YVSrr1vz+ipp7AQ== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.7.4" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-for-of@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.7.4.tgz#248800e3a5e507b1f103d8b4ca998e77c63932bc" + integrity sha512-zZ1fD1B8keYtEcKF+M1TROfeHTKnijcVQm0yO/Yu1f7qoDoxEIc/+GX6Go430Bg84eM/xwPFp0+h4EbZg7epAA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-function-name@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.7.4.tgz#75a6d3303d50db638ff8b5385d12451c865025b1" + integrity sha512-E/x09TvjHNhsULs2IusN+aJNRV5zKwxu1cpirZyRPw+FyyIKEHPXTsadj48bVpc1R5Qq1B5ZkzumuFLytnbT6g== + dependencies: + "@babel/helper-function-name" "^7.7.4" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-literals@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.7.4.tgz#27fe87d2b5017a2a5a34d1c41a6b9f6a6262643e" + integrity sha512-X2MSV7LfJFm4aZfxd0yLVFrEXAgPqYoDG53Br/tCKiKYfX0MjVjQeWPIhPHHsCqzwQANq+FLN786fF5rgLS+gw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-modules-amd@^7.2.0": + version "7.7.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.7.5.tgz#39e0fb717224b59475b306402bb8eedab01e729c" + integrity sha512-CT57FG4A2ZUNU1v+HdvDSDrjNWBrtCmSH6YbbgN3Lrf0Di/q/lWRxZrE72p3+HCCz9UjfZOEBdphgC0nzOS6DQ== + dependencies: + "@babel/helper-module-transforms" "^7.7.5" + "@babel/helper-plugin-utils" "^7.0.0" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-commonjs@^7.2.0": + version "7.7.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.5.tgz#1d27f5eb0bcf7543e774950e5b2fa782e637b345" + integrity sha512-9Cq4zTFExwFhQI6MT1aFxgqhIsMWQWDVwOgLzl7PTWJHsNaqFvklAU+Oz6AQLAS0dJKTwZSOCo20INwktxpi3Q== + dependencies: + "@babel/helper-module-transforms" "^7.7.5" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-simple-access" "^7.7.4" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-systemjs@^7.3.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.7.4.tgz#cd98152339d3e763dfe838b7d4273edaf520bb30" + integrity sha512-y2c96hmcsUi6LrMqvmNDPBBiGCiQu0aYqpHatVVu6kD4mFEXKjyNxd/drc18XXAf9dv7UXjrZwBVmTTGaGP8iw== + dependencies: + "@babel/helper-hoist-variables" "^7.7.4" + "@babel/helper-plugin-utils" "^7.0.0" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-umd@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.7.4.tgz#1027c355a118de0aae9fee00ad7813c584d9061f" + integrity sha512-u2B8TIi0qZI4j8q4C51ktfO7E3cQ0qnaXFI1/OXITordD40tt17g/sXqgNNCcMTcBFKrUPcGDx+TBJuZxLx7tw== + dependencies: + "@babel/helper-module-transforms" "^7.7.4" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.3.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.7.4.tgz#fb3bcc4ee4198e7385805007373d6b6f42c98220" + integrity sha512-jBUkiqLKvUWpv9GLSuHUFYdmHg0ujC1JEYoZUfeOOfNydZXp1sXObgyPatpcwjWgsdBGsagWW0cdJpX/DO2jMw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.7.4" + +"@babel/plugin-transform-new-target@^7.0.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.7.4.tgz#4a0753d2d60639437be07b592a9e58ee00720167" + integrity sha512-CnPRiNtOG1vRodnsyGX37bHQleHE14B9dnnlgSeEs3ek3fHN1A1SScglTCg1sfbe7sRQ2BUcpgpTpWSfMKz3gg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-object-super@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.7.4.tgz#48488937a2d586c0148451bf51af9d7dda567262" + integrity sha512-ho+dAEhC2aRnff2JCA0SAK7V2R62zJd/7dmtoe7MHcso4C2mS+vZjn1Pb1pCVZvJs1mgsvv5+7sT+m3Bysb6eg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-replace-supers" "^7.7.4" + +"@babel/plugin-transform-parameters@^7.2.0": + version "7.7.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.7.7.tgz#7a884b2460164dc5f194f668332736584c760007" + integrity sha512-OhGSrf9ZBrr1fw84oFXj5hgi8Nmg+E2w5L7NhnG0lPvpDtqd7dbyilM2/vR8CKbJ907RyxPh2kj6sBCSSfI9Ew== + dependencies: + "@babel/helper-call-delegate" "^7.7.4" + "@babel/helper-get-function-arity" "^7.7.4" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-regenerator@^7.3.4": + version "7.7.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.5.tgz#3a8757ee1a2780f390e89f246065ecf59c26fce9" + integrity sha512-/8I8tPvX2FkuEyWbjRCt4qTAgZK0DVy8QRguhA524UH48RfGJy94On2ri+dCuwOpcerPRl9O4ebQkRcVzIaGBw== + dependencies: + regenerator-transform "^0.14.0" + +"@babel/plugin-transform-runtime@^7.4.0": + version "7.7.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.7.6.tgz#4f2b548c88922fb98ec1c242afd4733ee3e12f61" + integrity sha512-tajQY+YmXR7JjTwRvwL4HePqoL3DYxpYXIHKVvrOIvJmeHe2y1w4tz5qz9ObUDC9m76rCzIMPyn4eERuwA4a4A== + dependencies: + "@babel/helper-module-imports" "^7.7.4" + "@babel/helper-plugin-utils" "^7.0.0" + resolve "^1.8.1" + semver "^5.5.1" + +"@babel/plugin-transform-shorthand-properties@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.7.4.tgz#74a0a9b2f6d67a684c6fbfd5f0458eb7ba99891e" + integrity sha512-q+suddWRfIcnyG5YiDP58sT65AJDZSUhXQDZE3r04AuqD6d/XLaQPPXSBzP2zGerkgBivqtQm9XKGLuHqBID6Q== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-spread@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.7.4.tgz#aa673b356fe6b7e70d69b6e33a17fef641008578" + integrity sha512-8OSs0FLe5/80cndziPlg4R0K6HcWSM0zyNhHhLsmw/Nc5MaA49cAsnoJ/t/YZf8qkG7fD+UjTRaApVDB526d7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-sticky-regex@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.7.4.tgz#ffb68c05090c30732076b1285dc1401b404a123c" + integrity sha512-Ls2NASyL6qtVe1H1hXts9yuEeONV2TJZmplLONkMPUG158CtmnrzW5Q5teibM5UVOFjG0D3IC5mzXR6pPpUY7A== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.0.0" + +"@babel/plugin-transform-template-literals@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.7.4.tgz#1eb6411736dd3fe87dbd20cc6668e5121c17d604" + integrity sha512-sA+KxLwF3QwGj5abMHkHgshp9+rRz+oY9uoRil4CyLtgEuE/88dpkeWgNk5qKVsJE9iSfly3nvHapdRiIS2wnQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.7.4" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-typeof-symbol@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.7.4.tgz#3174626214f2d6de322882e498a38e8371b2140e" + integrity sha512-KQPUQ/7mqe2m0B8VecdyaW5XcQYaePyl9R7IsKd+irzj6jvbhoGnRE+M0aNkyAzI07VfUQ9266L5xMARitV3wg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-unicode-regex@^7.2.0": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.7.4.tgz#a3c0f65b117c4c81c5b6484f2a5e7b95346b83ae" + integrity sha512-N77UUIV+WCvE+5yHw+oks3m18/umd7y392Zv7mYTpFqHtkpcc+QUz+gLJNTWVlWROIWeLqY0f3OjZxV5TcXnRw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.7.4" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/preset-env@^7.0.0 < 7.4.0": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.3.4.tgz#887cf38b6d23c82f19b5135298bdb160062e33e1" + integrity sha512-2mwqfYMK8weA0g0uBKOt4FE3iEodiHy9/CW0b+nWXcbL+pGzLx8ESYc+j9IIxr6LTDHWKgPm71i9smo02bw+gA== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-async-generator-functions" "^7.2.0" + "@babel/plugin-proposal-json-strings" "^7.2.0" + "@babel/plugin-proposal-object-rest-spread" "^7.3.4" + "@babel/plugin-proposal-optional-catch-binding" "^7.2.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.2.0" + "@babel/plugin-syntax-async-generators" "^7.2.0" + "@babel/plugin-syntax-json-strings" "^7.2.0" + "@babel/plugin-syntax-object-rest-spread" "^7.2.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" + "@babel/plugin-transform-arrow-functions" "^7.2.0" + "@babel/plugin-transform-async-to-generator" "^7.3.4" + "@babel/plugin-transform-block-scoped-functions" "^7.2.0" + "@babel/plugin-transform-block-scoping" "^7.3.4" + "@babel/plugin-transform-classes" "^7.3.4" + "@babel/plugin-transform-computed-properties" "^7.2.0" + "@babel/plugin-transform-destructuring" "^7.2.0" + "@babel/plugin-transform-dotall-regex" "^7.2.0" + "@babel/plugin-transform-duplicate-keys" "^7.2.0" + "@babel/plugin-transform-exponentiation-operator" "^7.2.0" + "@babel/plugin-transform-for-of" "^7.2.0" + "@babel/plugin-transform-function-name" "^7.2.0" + "@babel/plugin-transform-literals" "^7.2.0" + "@babel/plugin-transform-modules-amd" "^7.2.0" + "@babel/plugin-transform-modules-commonjs" "^7.2.0" + "@babel/plugin-transform-modules-systemjs" "^7.3.4" + "@babel/plugin-transform-modules-umd" "^7.2.0" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.3.0" + "@babel/plugin-transform-new-target" "^7.0.0" + "@babel/plugin-transform-object-super" "^7.2.0" + "@babel/plugin-transform-parameters" "^7.2.0" + "@babel/plugin-transform-regenerator" "^7.3.4" + "@babel/plugin-transform-shorthand-properties" "^7.2.0" + "@babel/plugin-transform-spread" "^7.2.0" + "@babel/plugin-transform-sticky-regex" "^7.2.0" + "@babel/plugin-transform-template-literals" "^7.2.0" + "@babel/plugin-transform-typeof-symbol" "^7.2.0" + "@babel/plugin-transform-unicode-regex" "^7.2.0" + browserslist "^4.3.4" + invariant "^2.2.2" + js-levenshtein "^1.1.3" + semver "^5.3.0" + +"@babel/runtime-corejs2@^7.2.0": + version "7.7.7" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.7.7.tgz#44885957b275a5fd70413142eda9cff4662847ac" + integrity sha512-P91T3dFYQL7aj44PxOMIAbo66Ag3NbmXG9fseSYaXxapp3K9XTct5HU9IpTOm2D0AoktKusgqzN5YcSxZXEKBQ== + dependencies: + core-js "^2.6.5" + regenerator-runtime "^0.13.2" + +"@babel/runtime@^7.0.0": + version "7.7.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.7.tgz#194769ca8d6d7790ec23605af9ee3e42a0aa79cf" + integrity sha512-uCnC2JEVAu8AKB5do1WRIsvrdJ0flYx/A/9f/6chdacnEZ7LmavjdsDXr5ksYBegxtuTPR5Va9/+13QF/kFkCA== + dependencies: + regenerator-runtime "^0.13.2" + +"@babel/template@^7.4.0", "@babel/template@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.7.4.tgz#428a7d9eecffe27deac0a98e23bf8e3675d2a77b" + integrity sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.7.4" + "@babel/types" "^7.7.4" + +"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.7.4.tgz#9c1e7c60fb679fe4fcfaa42500833333c2058558" + integrity sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw== + dependencies: + "@babel/code-frame" "^7.5.5" + "@babel/generator" "^7.7.4" + "@babel/helper-function-name" "^7.7.4" + "@babel/helper-split-export-declaration" "^7.7.4" + "@babel/parser" "^7.7.4" + "@babel/types" "^7.7.4" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.13" + +"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.7.4.tgz#516570d539e44ddf308c07569c258ff94fde9193" + integrity sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA== + dependencies: + esutils "^2.0.2" + lodash "^4.17.13" + to-fast-properties "^2.0.0" + +"@cnakazawa/watch@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef" + integrity sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA== + dependencies: + exec-sh "^0.3.2" + minimist "^1.2.0" + +"@cypress/listr-verbose-renderer@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#a77492f4b11dcc7c446a34b3e28721afd33c642a" + integrity sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo= + dependencies: + chalk "^1.1.3" + cli-cursor "^1.0.2" + date-fns "^1.27.2" + figures "^1.7.0" + +"@cypress/xvfb@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a" + integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q== + dependencies: + debug "^3.1.0" + lodash.once "^4.1.1" + +"@hapi/address@2.x.x": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" + integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ== + +"@hapi/bourne@1.x.x": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-1.3.2.tgz#0a7095adea067243ce3283e1b56b8a8f453b242a" + integrity sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA== + +"@hapi/hoek@8.x.x", "@hapi/hoek@^8.3.0": + version "8.5.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.0.tgz#2f9ce301c8898e1c3248b0a8564696b24d1a9a5a" + integrity sha512-7XYT10CZfPsH7j9F1Jmg1+d0ezOux2oM2GfArAzLwWe4mE2Dr3hVjsAL6+TFY49RRJlCdJDMw3nJsLFroTc8Kw== + +"@hapi/joi@^15.0.1": + version "15.1.1" + resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-15.1.1.tgz#c675b8a71296f02833f8d6d243b34c57b8ce19d7" + integrity sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ== + dependencies: + "@hapi/address" "2.x.x" + "@hapi/bourne" "1.x.x" + "@hapi/hoek" "8.x.x" + "@hapi/topo" "3.x.x" + +"@hapi/topo@3.x.x": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.6.tgz#68d935fa3eae7fdd5ab0d7f953f3205d8b2bfc29" + integrity sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ== + dependencies: + "@hapi/hoek" "^8.3.0" + +"@intervolga/optimize-cssnano-plugin@^1.0.5": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@intervolga/optimize-cssnano-plugin/-/optimize-cssnano-plugin-1.0.6.tgz#be7c7846128b88f6a9b1d1261a0ad06eb5c0fdf8" + integrity sha512-zN69TnSr0viRSU6cEDIcuPcP67QcpQ6uHACg58FiN9PDrU6SLyGW3MR4tiISbYxy1kDWAVPwD+XwQTWE5cigAA== + dependencies: + cssnano "^4.0.0" + cssnano-preset-default "^4.0.0" + postcss "^7.0.0" + +"@jest/console@^24.7.1", "@jest/console@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" + integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== + dependencies: + "@jest/source-map" "^24.9.0" + chalk "^2.0.1" + slash "^2.0.0" + +"@jest/core@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.9.0.tgz#2ceccd0b93181f9c4850e74f2a9ad43d351369c4" + integrity sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A== + dependencies: + "@jest/console" "^24.7.1" + "@jest/reporters" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + ansi-escapes "^3.0.0" + chalk "^2.0.1" + exit "^0.1.2" + graceful-fs "^4.1.15" + jest-changed-files "^24.9.0" + jest-config "^24.9.0" + jest-haste-map "^24.9.0" + jest-message-util "^24.9.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.9.0" + jest-resolve-dependencies "^24.9.0" + jest-runner "^24.9.0" + jest-runtime "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + jest-watcher "^24.9.0" + micromatch "^3.1.10" + p-each-series "^1.0.0" + realpath-native "^1.1.0" + rimraf "^2.5.4" + slash "^2.0.0" + strip-ansi "^5.0.0" + +"@jest/environment@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.9.0.tgz#21e3afa2d65c0586cbd6cbefe208bafade44ab18" + integrity sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ== + dependencies: + "@jest/fake-timers" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + +"@jest/fake-timers@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.9.0.tgz#ba3e6bf0eecd09a636049896434d306636540c93" + integrity sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A== + dependencies: + "@jest/types" "^24.9.0" + jest-message-util "^24.9.0" + jest-mock "^24.9.0" + +"@jest/reporters@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.9.0.tgz#86660eff8e2b9661d042a8e98a028b8d631a5b43" + integrity sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw== + dependencies: + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + exit "^0.1.2" + glob "^7.1.2" + istanbul-lib-coverage "^2.0.2" + istanbul-lib-instrument "^3.0.1" + istanbul-lib-report "^2.0.4" + istanbul-lib-source-maps "^3.0.1" + istanbul-reports "^2.2.6" + jest-haste-map "^24.9.0" + jest-resolve "^24.9.0" + jest-runtime "^24.9.0" + jest-util "^24.9.0" + jest-worker "^24.6.0" + node-notifier "^5.4.2" + slash "^2.0.0" + source-map "^0.6.0" + string-length "^2.0.0" + +"@jest/source-map@^24.3.0", "@jest/source-map@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" + integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.1.15" + source-map "^0.6.0" + +"@jest/test-result@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" + integrity sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA== + dependencies: + "@jest/console" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/istanbul-lib-coverage" "^2.0.0" + +"@jest/test-sequencer@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz#f8f334f35b625a4f2f355f2fe7e6036dad2e6b31" + integrity sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A== + dependencies: + "@jest/test-result" "^24.9.0" + jest-haste-map "^24.9.0" + jest-runner "^24.9.0" + jest-runtime "^24.9.0" + +"@jest/transform@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.9.0.tgz#4ae2768b296553fadab09e9ec119543c90b16c56" + integrity sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^24.9.0" + babel-plugin-istanbul "^5.1.0" + chalk "^2.0.1" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.1.15" + jest-haste-map "^24.9.0" + jest-regex-util "^24.9.0" + jest-util "^24.9.0" + micromatch "^3.1.10" + pirates "^4.0.1" + realpath-native "^1.1.0" + slash "^2.0.0" + source-map "^0.6.1" + write-file-atomic "2.4.1" + +"@jest/types@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" + integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^1.1.1" + "@types/yargs" "^13.0.0" + +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== + +"@soda/friendly-errors-webpack-plugin@^1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.1.tgz#706f64bcb4a8b9642b48ae3ace444c70334d615d" + integrity sha512-cWKrGaFX+rfbMrAxVv56DzhPNqOJPZuNIS2HGMELtgGzb+vsMzyig9mml5gZ/hr2BGtSLV+dP2LUEuAL8aG2mQ== + dependencies: + chalk "^1.1.3" + error-stack-parser "^2.0.0" + string-width "^2.0.0" + +"@types/babel__core@^7.1.0": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.3.tgz#e441ea7df63cd080dfcd02ab199e6d16a735fc30" + integrity sha512-8fBo0UR2CcwWxeX7WIIgJ7lXjasFxoYgRnFHUj+hRvKkpiBJbxhdAPTCY6/ZKM0uxANFVzt4yObSLuTiTnazDA== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.1" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.1.tgz#4901767b397e8711aeb99df8d396d7ba7b7f0e04" + integrity sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.2.tgz#4ff63d6b52eddac1de7b975a5223ed32ecea9307" + integrity sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.0.8" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.8.tgz#479a4ee3e291a403a1096106013ec22cf9b64012" + integrity sha512-yGeB2dHEdvxjP0y4UbRtQaSkXJ9649fYCmIdRoul5kfAoGCwxuCbMhag0k3RPfnuh9kPGm8x89btcfDEXdVWGw== + dependencies: + "@babel/types" "^7.3.0" + +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + +"@types/eslint-visitor-keys@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" + integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== + +"@types/events@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" + integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== + +"@types/glob@^7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" + integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== + dependencies: + "@types/events" "*" + "@types/minimatch" "*" + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" + integrity sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg== + +"@types/istanbul-lib-report@*": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz#e5471e7fa33c61358dd38426189c037a58433b8c" + integrity sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz#7a8cbf6a406f36c8add871625b278eaf0b0d255a" + integrity sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA== + dependencies: + "@types/istanbul-lib-coverage" "*" + "@types/istanbul-lib-report" "*" + +"@types/jest@^24.0.18": + version "24.0.25" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.25.tgz#2aba377824ce040114aa906ad2cac2c85351360f" + integrity sha512-hnP1WpjN4KbGEK4dLayul6lgtys6FPz0UfxMeMQCv0M+sTnzN3ConfiO72jHgLxl119guHgI8gLqDOrRLsyp2g== + dependencies: + jest-diff "^24.3.0" + +"@types/js-cookie@^2.2.2": + version "2.2.4" + resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.4.tgz#f79720b4755aa197c2e15e982e2f438f5748e348" + integrity sha512-WTfSE1Eauak/Nrg6cA9FgPTFvVawejsai6zXoq0QYTQ3mxONeRtGhKxa7wMlUzWWmzrmTeV+rwLjHgsCntdrsA== + +"@types/json-schema@^7.0.3": + version "7.0.4" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" + integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== + +"@types/minimatch@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== + +"@types/node@*": + version "13.1.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.1.2.tgz#fe94285bf5e0782e1a9e5a8c482b1c34465fa385" + integrity sha512-B8emQA1qeKerqd1dmIsQYnXi+mmAzTB7flExjmy5X1aVAKFNNNDubkavwR13kR6JnpeLp3aLoJhwn9trWPAyFQ== + +"@types/node@~12.12.23": + version "12.12.23" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.23.tgz#e82e3b4dca7e840b53ebbefd3a0f3d4ddfc332fa" + integrity sha512-mHScXQHFSGNmJQq8O3nQMUA8IBtRxXCwBlSpxaPRFaSsFl7HxNuk569j6WUzjcud6Ew5LIHhuGQ/kAH7J0E7zA== + +"@types/normalize-package-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" + integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== + +"@types/nprogress@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@types/nprogress/-/nprogress-0.2.0.tgz#86c593682d4199212a0509cc3c4d562bbbd6e45f" + integrity sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A== + +"@types/q@^1.5.1": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" + integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== + +"@types/sizzle@2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" + integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== + +"@types/stack-utils@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" + integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== + +"@types/strip-bom@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" + integrity sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I= + +"@types/strip-json-comments@0.0.30": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" + integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== + +"@types/webpack-env@^1.13.9", "@types/webpack-env@^1.14.0": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.14.1.tgz#0d8a53f308f017c53a5ddc3d07f4d6fa76b790d7" + integrity sha512-0Ki9jAAhKDSuLDXOIMADg54Hu60SuBTEsWaJGGy5cV+SSUQ63J2a+RrYYGrErzz39fXzTibhKrAQJAb8M7PNcA== + +"@types/yargs-parser@*": + version "13.1.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.1.0.tgz#c563aa192f39350a1d18da36c5a8da382bbd8228" + integrity sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg== + +"@types/yargs@^13.0.0": + version "13.0.4" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.4.tgz#53d231cebe1a540e7e13727fc1f0d13ad4a9ba3b" + integrity sha512-Ke1WmBbIkVM8bpvsNEcGgQM70XcEh/nbpxQhW7FhrsbCsXSY9BmLB1+LHtD7r9zrsOcFlLiF+a/UeJsdfw3C5A== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@^1.1.0": + version "1.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.13.0.tgz#22fed9b16ddfeb402fd7bcde56307820f6ebc49f" + integrity sha512-WQHCozMnuNADiqMtsNzp96FNox5sOVpU8Xt4meaT4em8lOG1SrOv92/mUbEHQVh90sldKSfcOc/I0FOb/14G1g== + dependencies: + "@typescript-eslint/experimental-utils" "1.13.0" + eslint-utils "^1.3.1" + functional-red-black-tree "^1.0.1" + regexpp "^2.0.1" + tsutils "^3.7.0" + +"@typescript-eslint/experimental-utils@1.13.0": + version "1.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz#b08c60d780c0067de2fb44b04b432f540138301e" + integrity sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/typescript-estree" "1.13.0" + eslint-scope "^4.0.0" + +"@typescript-eslint/parser@^1.1.0": + version "1.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-1.13.0.tgz#61ac7811ea52791c47dc9fd4dd4a184fae9ac355" + integrity sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ== + dependencies: + "@types/eslint-visitor-keys" "^1.0.0" + "@typescript-eslint/experimental-utils" "1.13.0" + "@typescript-eslint/typescript-estree" "1.13.0" + eslint-visitor-keys "^1.0.0" + +"@typescript-eslint/typescript-estree@1.13.0": + version "1.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz#8140f17d0f60c03619798f1d628b8434913dc32e" + integrity sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw== + dependencies: + lodash.unescape "4.0.1" + semver "5.5.0" + +"@vue/babel-helper-vue-jsx-merge-props@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.0.0.tgz#048fe579958da408fb7a8b2a3ec050b50a661040" + integrity sha512-6tyf5Cqm4m6v7buITuwS+jHzPlIPxbFzEhXR5JGZpbrvOcp1hiQKckd305/3C7C36wFekNTQSxAtgeM0j0yoUw== + +"@vue/babel-plugin-transform-vue-jsx@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.1.2.tgz#c0a3e6efc022e75e4247b448a8fc6b86f03e91c0" + integrity sha512-YfdaoSMvD1nj7+DsrwfTvTnhDXI7bsuh+Y5qWwvQXlD24uLgnsoww3qbiZvWf/EoviZMrvqkqN4CBw0W3BWUTQ== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.2.0" + "@vue/babel-helper-vue-jsx-merge-props" "^1.0.0" + html-tags "^2.0.0" + lodash.kebabcase "^4.1.1" + svg-tags "^1.0.0" + +"@vue/babel-preset-app@^3.12.1": + version "3.12.1" + resolved "https://registry.yarnpkg.com/@vue/babel-preset-app/-/babel-preset-app-3.12.1.tgz#24c477052f078f30fdb7735103b14dd1fa2cbfe1" + integrity sha512-Zjy5jQaikV1Pz+ri0YgXFS7q4/5wCxB5tRkDOEIt5+4105u0Feb/pvH20nVL6nx9GyXrECFfcm7Yxr/z++OaPQ== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/plugin-proposal-class-properties" "^7.0.0" + "@babel/plugin-proposal-decorators" "^7.1.0" + "@babel/plugin-syntax-dynamic-import" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.0.0" + "@babel/plugin-transform-runtime" "^7.4.0" + "@babel/preset-env" "^7.0.0 < 7.4.0" + "@babel/runtime" "^7.0.0" + "@babel/runtime-corejs2" "^7.2.0" + "@vue/babel-preset-jsx" "^1.0.0" + babel-plugin-dynamic-import-node "^2.2.0" + babel-plugin-module-resolver "3.2.0" + core-js "^2.6.5" + +"@vue/babel-preset-jsx@^1.0.0": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@vue/babel-preset-jsx/-/babel-preset-jsx-1.1.2.tgz#2e169eb4c204ea37ca66c2ea85a880bfc99d4f20" + integrity sha512-zDpVnFpeC9YXmvGIDSsKNdL7qCG2rA3gjywLYHPCKDT10erjxF4U+6ay9X6TW5fl4GsDlJp9bVfAVQAAVzxxvQ== + dependencies: + "@vue/babel-helper-vue-jsx-merge-props" "^1.0.0" + "@vue/babel-plugin-transform-vue-jsx" "^1.1.2" + "@vue/babel-sugar-functional-vue" "^1.1.2" + "@vue/babel-sugar-inject-h" "^1.1.2" + "@vue/babel-sugar-v-model" "^1.1.2" + "@vue/babel-sugar-v-on" "^1.1.2" + +"@vue/babel-sugar-functional-vue@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.1.2.tgz#f7e24fba09e6f1ee70104560a8808057555f1a9a" + integrity sha512-YhmdJQSVEFF5ETJXzrMpj0nkCXEa39TvVxJTuVjzvP2rgKhdMmQzlJuMv/HpadhZaRVMCCF3AEjjJcK5q/cYzQ== + dependencies: + "@babel/plugin-syntax-jsx" "^7.2.0" + +"@vue/babel-sugar-inject-h@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.1.2.tgz#8a5276b6d8e2ed16ffc8078aad94236274e6edf0" + integrity sha512-VRSENdTvD5htpnVp7i7DNuChR5rVMcORdXjvv5HVvpdKHzDZAYiLSD+GhnhxLm3/dMuk8pSzV+k28ECkiN5m8w== + dependencies: + "@babel/plugin-syntax-jsx" "^7.2.0" + +"@vue/babel-sugar-v-model@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.1.2.tgz#1ff6fd1b800223fc9cb1e84dceb5e52d737a8192" + integrity sha512-vLXPvNq8vDtt0u9LqFdpGM9W9IWDmCmCyJXuozlq4F4UYVleXJ2Fa+3JsnTZNJcG+pLjjfnEGHci2339Kj5sGg== + dependencies: + "@babel/plugin-syntax-jsx" "^7.2.0" + "@vue/babel-helper-vue-jsx-merge-props" "^1.0.0" + "@vue/babel-plugin-transform-vue-jsx" "^1.1.2" + camelcase "^5.0.0" + html-tags "^2.0.0" + svg-tags "^1.0.0" + +"@vue/babel-sugar-v-on@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.1.2.tgz#b2ef99b8f2fab09fbead25aad70ef42e1cf5b13b" + integrity sha512-T8ZCwC8Jp2uRtcZ88YwZtZXe7eQrJcfRq0uTFy6ShbwYJyz5qWskRFoVsdTi9o0WEhmQXxhQUewodOSCUPVmsQ== + dependencies: + "@babel/plugin-syntax-jsx" "^7.2.0" + "@vue/babel-plugin-transform-vue-jsx" "^1.1.2" + camelcase "^5.0.0" + +"@vue/cli-overlay@^3.12.1": + version "3.12.1" + resolved "https://registry.yarnpkg.com/@vue/cli-overlay/-/cli-overlay-3.12.1.tgz#bdfde8f7123561ab06e4e4c60b854cc5092f5ab1" + integrity sha512-Bym92EN+lj+cNRN2ozbYyH+V8DMXWGbCDUk+hiJ4EYDBZfBkZKvalk1/mOBFwyxiopnnbOEBAAhL/UuMQ1xARg== + +"@vue/cli-plugin-babel@^3.11.0": + version "3.12.1" + resolved "https://registry.yarnpkg.com/@vue/cli-plugin-babel/-/cli-plugin-babel-3.12.1.tgz#9a79159de8cd086b013fa6d78a39830b2e2ec706" + integrity sha512-Zetvz8PikLCGomeKOKu8pC9YQ7cfxs7pGpvEOzaxGdhMnebhjAYR6i6dOB57A6N5lhxQksXCtYTv26QgfiIpdg== + dependencies: + "@babel/core" "^7.0.0" + "@vue/babel-preset-app" "^3.12.1" + "@vue/cli-shared-utils" "^3.12.1" + babel-loader "^8.0.5" + webpack "^4.0.0" + +"@vue/cli-plugin-e2e-cypress@^3.11.0": + version "3.12.1" + resolved "https://registry.yarnpkg.com/@vue/cli-plugin-e2e-cypress/-/cli-plugin-e2e-cypress-3.12.1.tgz#7cc52a11f159fdbcf61ecac244ede54bfd663ac9" + integrity sha512-2BYYhXTkT9+rXKv8OfvpuEbX7KYo0Ma4vNTjFG62W+CkjO+R10mYwJvRMaXUFaV6iRrkKQab5oCuNMCnefqVKg== + dependencies: + "@vue/cli-shared-utils" "^3.12.1" + cypress "^3.2.0" + eslint-plugin-cypress "^2.2.1" + +"@vue/cli-plugin-eslint@^3.11.0": + version "3.12.1" + resolved "https://registry.yarnpkg.com/@vue/cli-plugin-eslint/-/cli-plugin-eslint-3.12.1.tgz#302c463867f38e790bb996eafdf7159c782dc8cf" + integrity sha512-tVTZlEZsy3sQbO4LLWFK11yzlWwqVAqaM+IY+BeWHITBzEJKh2KmouG+x6x/reXiU3qROsMJ4Ej3Hs8buSMWyQ== + dependencies: + "@vue/cli-shared-utils" "^3.12.1" + babel-eslint "^10.0.1" + eslint-loader "^2.1.2" + globby "^9.2.0" + webpack "^4.0.0" + yorkie "^2.0.0" + optionalDependencies: + eslint "^4.19.1" + eslint-plugin-vue "^4.7.1" + +"@vue/cli-plugin-pwa@^3.11.0": + version "3.12.1" + resolved "https://registry.yarnpkg.com/@vue/cli-plugin-pwa/-/cli-plugin-pwa-3.12.1.tgz#03c9978b640a3cbce02b7922103a131a88063f31" + integrity sha512-ver9mJ1t4gqXeB4CmTAYYqnqx8zDzo6ORwWK+iMTvNI2aOrJReh7QTdtWpQbkqHSzN4jNUtYSLIsP/ONdrQOhw== + dependencies: + "@vue/cli-shared-utils" "^3.12.1" + webpack "^4.0.0" + workbox-webpack-plugin "^3.6.3" + +"@vue/cli-plugin-typescript@^3.11.0": + version "3.12.1" + resolved "https://registry.yarnpkg.com/@vue/cli-plugin-typescript/-/cli-plugin-typescript-3.12.1.tgz#71b306983de37a03c43860ac035bd0a15eb29d27" + integrity sha512-sh+WKbpsDw6wOrpM4FSD1xKXpyp8mVcl+yyEk+WvJuuSdfwueRubAM7uYbrOGtNSOegpZqBwbNxEO4FIUBeLKQ== + dependencies: + "@types/webpack-env" "^1.13.9" + "@vue/cli-shared-utils" "^3.12.1" + fork-ts-checker-webpack-plugin "^0.5.2" + globby "^9.2.0" + ts-loader "^5.3.3" + tslint "^5.15.0" + webpack "^4.0.0" + yorkie "^2.0.0" + +"@vue/cli-plugin-unit-jest@^3.11.0": + version "3.12.1" + resolved "https://registry.yarnpkg.com/@vue/cli-plugin-unit-jest/-/cli-plugin-unit-jest-3.12.1.tgz#b7edd30701191deaa9d53cea752b8d72b825d640" + integrity sha512-Cc9Kq4+RaUN1yfNVb7c9hVDNXo2tFTWHgwooCL3XWMu2iL+pDawQt8ZeSqauDY95JoMeEAVy2xBimjL+7jo/jw== + dependencies: + "@vue/cli-shared-utils" "^3.12.1" + babel-jest "^23.6.0" + babel-plugin-transform-es2015-modules-commonjs "^6.26.2" + jest "^23.6.0" + jest-serializer-vue "^2.0.2" + jest-transform-stub "^2.0.0" + jest-watch-typeahead "0.2.1" + vue-jest "^3.0.4" + +"@vue/cli-service@^3.11.0": + version "3.12.1" + resolved "https://registry.yarnpkg.com/@vue/cli-service/-/cli-service-3.12.1.tgz#13220b1c189254e7c003390df329086f9b6e77e6" + integrity sha512-PDxNrTGnSKzeV1ruFlsRIAO8JcPizwT0EJXq9GeyooU+p+sOkv7aKkCBJQVYNjZapD1NOGWx6CvAAC/wAW+gew== + dependencies: + "@intervolga/optimize-cssnano-plugin" "^1.0.5" + "@soda/friendly-errors-webpack-plugin" "^1.7.1" + "@vue/cli-overlay" "^3.12.1" + "@vue/cli-shared-utils" "^3.12.1" + "@vue/component-compiler-utils" "^3.0.0" + "@vue/preload-webpack-plugin" "^1.1.0" + "@vue/web-component-wrapper" "^1.2.0" + acorn "^6.1.1" + acorn-walk "^6.1.1" + address "^1.0.3" + autoprefixer "^9.5.1" + browserslist "^4.5.4" + cache-loader "^2.0.1" + case-sensitive-paths-webpack-plugin "^2.2.0" + chalk "^2.4.2" + cli-highlight "^2.1.0" + clipboardy "^2.0.0" + cliui "^5.0.0" + copy-webpack-plugin "^4.6.0" + css-loader "^1.0.1" + cssnano "^4.1.10" + current-script-polyfill "^1.0.0" + debug "^4.1.1" + default-gateway "^5.0.2" + dotenv "^7.0.0" + dotenv-expand "^5.1.0" + escape-string-regexp "^1.0.5" + file-loader "^3.0.1" + fs-extra "^7.0.1" + globby "^9.2.0" + hash-sum "^1.0.2" + html-webpack-plugin "^3.2.0" + launch-editor-middleware "^2.2.1" + lodash.defaultsdeep "^4.6.1" + lodash.mapvalues "^4.6.0" + lodash.transform "^4.6.0" + mini-css-extract-plugin "^0.8.0" + minimist "^1.2.0" + ora "^3.4.0" + portfinder "^1.0.20" + postcss-loader "^3.0.0" + read-pkg "^5.0.0" + semver "^6.0.0" + slash "^2.0.0" + source-map-url "^0.4.0" + ssri "^6.0.1" + string.prototype.padend "^3.0.0" + terser-webpack-plugin "^1.2.3" + thread-loader "^2.1.2" + url-loader "^1.1.2" + vue-loader "^15.7.0" + webpack "^4.0.0" + webpack-bundle-analyzer "^3.3.0" + webpack-chain "^4.11.0" + webpack-dev-server "^3.4.1" + webpack-merge "^4.2.1" + +"@vue/cli-shared-utils@^3.12.1": + version "3.12.1" + resolved "https://registry.yarnpkg.com/@vue/cli-shared-utils/-/cli-shared-utils-3.12.1.tgz#bcf076287ddadeebbb97c6a748dfe9ff50ec8df0" + integrity sha512-jFblzRFjutGwu5utOKdVlPlsbA1lBUNNQlAThzNqej+JtTKJjnvjlhjKX0Gq0oOny5FjKWhoyfQ74p9h1qE6JQ== + dependencies: + "@hapi/joi" "^15.0.1" + chalk "^2.4.1" + execa "^1.0.0" + launch-editor "^2.2.1" + lru-cache "^5.1.1" + node-ipc "^9.1.1" + open "^6.3.0" + ora "^3.4.0" + request "^2.87.0" + request-promise-native "^1.0.7" + semver "^6.0.0" + string.prototype.padstart "^3.0.0" + +"@vue/component-compiler-utils@^3.0.0", "@vue/component-compiler-utils@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-3.1.0.tgz#64cd394925f5af1f9c3228c66e954536f5311857" + integrity sha512-OJ7swvl8LtKtX5aYP8jHhO6fQBIRIGkU6rvWzK+CGJiNOnvg16nzcBkd9qMZzW8trI2AsqAKx263nv7kb5rhZw== + dependencies: + consolidate "^0.15.1" + hash-sum "^1.0.2" + lru-cache "^4.1.2" + merge-source-map "^1.1.0" + postcss "^7.0.14" + postcss-selector-parser "^5.0.0" + prettier "^1.18.2" + source-map "~0.6.1" + vue-template-es2015-compiler "^1.9.0" + +"@vue/eslint-config-standard@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@vue/eslint-config-standard/-/eslint-config-standard-4.0.0.tgz#6be447ee674e3b0f733c584098fd9a22e6d76fcd" + integrity sha512-bQghq1cw1BuMRHNhr3tRpAJx1tpGy0QtajQX873kLtA9YVuOIoXR7nAWnTN09bBHnSUh2N288vMsqPi2fI4Hzg== + dependencies: + eslint-config-standard "^12.0.0" + eslint-plugin-import "^2.14.0" + eslint-plugin-node "^8.0.0" + eslint-plugin-promise "^4.0.1" + eslint-plugin-standard "^4.0.0" + +"@vue/eslint-config-typescript@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@vue/eslint-config-typescript/-/eslint-config-typescript-4.0.0.tgz#a202983598a4a826460cbb8ee43826875b0f6673" + integrity sha512-uSMAMgw4xDgVdZQhpbtJRo8nMV4oOy3Ht8olfOo7xvYFYLMF2JZ1tDRKd9/NSusxA72O2Vma+HzmyzDHg9evcQ== + dependencies: + "@typescript-eslint/eslint-plugin" "^1.1.0" + "@typescript-eslint/parser" "^1.1.0" + +"@vue/preload-webpack-plugin@^1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.1.tgz#18723530d304f443021da2292d6ec9502826104a" + integrity sha512-8VCoJeeH8tCkzhkpfOkt+abALQkS11OIHhte5MBzYaKMTqK0A3ZAKEUVAffsOklhEv7t0yrQt696Opnu9oAx+w== + +"@vue/test-utils@^1.0.0-beta.29": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.0.0-beta.30.tgz#d5f26d1e2411fdb7fa7fdedb61b4b4ea4194c49d" + integrity sha512-Wyvcha9fNk8+kzTDwb3xWGjPkCPzHSYSwKP6MplrPTG/auhqoad7JqUEceZLc6u7AU4km2pPQ8/m9s0RgCZ0NA== + dependencies: + dom-event-types "^1.0.0" + lodash "^4.17.15" + pretty "^2.0.0" + +"@vue/web-component-wrapper@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@vue/web-component-wrapper/-/web-component-wrapper-1.2.0.tgz#bb0e46f1585a7e289b4ee6067dcc5a6ae62f1dd1" + integrity sha512-Xn/+vdm9CjuC9p3Ae+lTClNutrVhsXpzxvoTXXtoys6kVRX9FkueSUAqSWAyZntmVLlR4DosBV4pH8y5Z/HbUw== + +"@webassemblyjs/ast@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" + integrity sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ== + dependencies: + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/wast-parser" "1.8.5" + +"@webassemblyjs/floating-point-hex-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz#1ba926a2923613edce496fd5b02e8ce8a5f49721" + integrity sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ== + +"@webassemblyjs/helper-api-error@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz#c49dad22f645227c5edb610bdb9697f1aab721f7" + integrity sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA== + +"@webassemblyjs/helper-buffer@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz#fea93e429863dd5e4338555f42292385a653f204" + integrity sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q== + +"@webassemblyjs/helper-code-frame@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz#9a740ff48e3faa3022b1dff54423df9aa293c25e" + integrity sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ== + dependencies: + "@webassemblyjs/wast-printer" "1.8.5" + +"@webassemblyjs/helper-fsm@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz#ba0b7d3b3f7e4733da6059c9332275d860702452" + integrity sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow== + +"@webassemblyjs/helper-module-context@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz#def4b9927b0101dc8cbbd8d1edb5b7b9c82eb245" + integrity sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g== + dependencies: + "@webassemblyjs/ast" "1.8.5" + mamacro "^0.0.3" + +"@webassemblyjs/helper-wasm-bytecode@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz#537a750eddf5c1e932f3744206551c91c1b93e61" + integrity sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ== + +"@webassemblyjs/helper-wasm-section@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz#74ca6a6bcbe19e50a3b6b462847e69503e6bfcbf" + integrity sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + +"@webassemblyjs/ieee754@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz#712329dbef240f36bf57bd2f7b8fb9bf4154421e" + integrity sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz#044edeb34ea679f3e04cd4fd9824d5e35767ae10" + integrity sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz#a8bf3b5d8ffe986c7c1e373ccbdc2a0915f0cedc" + integrity sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw== + +"@webassemblyjs/wasm-edit@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz#962da12aa5acc1c131c81c4232991c82ce56e01a" + integrity sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/helper-wasm-section" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + "@webassemblyjs/wasm-opt" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + "@webassemblyjs/wast-printer" "1.8.5" + +"@webassemblyjs/wasm-gen@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz#54840766c2c1002eb64ed1abe720aded714f98bc" + integrity sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/ieee754" "1.8.5" + "@webassemblyjs/leb128" "1.8.5" + "@webassemblyjs/utf8" "1.8.5" + +"@webassemblyjs/wasm-opt@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz#b24d9f6ba50394af1349f510afa8ffcb8a63d264" + integrity sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + +"@webassemblyjs/wasm-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz#21576f0ec88b91427357b8536383668ef7c66b8d" + integrity sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-api-error" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/ieee754" "1.8.5" + "@webassemblyjs/leb128" "1.8.5" + "@webassemblyjs/utf8" "1.8.5" + +"@webassemblyjs/wast-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz#e10eecd542d0e7bd394f6827c49f3df6d4eefb8c" + integrity sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/floating-point-hex-parser" "1.8.5" + "@webassemblyjs/helper-api-error" "1.8.5" + "@webassemblyjs/helper-code-frame" "1.8.5" + "@webassemblyjs/helper-fsm" "1.8.5" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/wast-printer@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz#114bbc481fd10ca0e23b3560fa812748b0bae5bc" + integrity sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/wast-parser" "1.8.5" + "@xtuc/long" "4.2.2" + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +abab@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" + integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn-globals@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" + integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== + dependencies: + acorn "^6.0.1" + acorn-walk "^6.0.1" + +acorn-jsx@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" + integrity sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s= + dependencies: + acorn "^3.0.4" + +acorn-jsx@^5.0.0, acorn-jsx@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" + integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw== + +acorn-walk@^6.0.1, acorn-walk@^6.1.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" + integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== + +acorn@^3.0.4: + version "3.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" + integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= + +acorn@^5.5.0, acorn@^5.5.3: + version "5.7.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" + integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== + +acorn@^6.0.1, acorn@^6.0.2, acorn@^6.0.7, acorn@^6.1.1, acorn@^6.2.1: + version "6.4.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.0.tgz#b659d2ffbafa24baf5db1cdbb2c94a983ecd2784" + integrity sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw== + +acorn@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" + integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ== + +address@^1.0.3: + version "1.1.2" + resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" + integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== + +ajv-errors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" + integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== + +ajv-keywords@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" + integrity sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I= + +ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" + integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== + +ajv@^5.2.3, ajv@^5.3.0: + version "5.5.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" + integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= + dependencies: + co "^4.6.0" + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5: + version "6.10.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" + integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +alphanum-sort@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= + +ansi-colors@^3.0.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" + integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== + +ansi-escapes@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" + integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= + +ansi-escapes@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-escapes@^4.2.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.0.tgz#a4ce2b33d6b214b7950d8595c212f12ac9cc569d" + integrity sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg== + dependencies: + type-fest "^0.8.1" + +ansi-html@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" + integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.0.0, ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +append-transform@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" + integrity sha1-126/jKlNJ24keja61EpLdKthGZE= + dependencies: + default-require-extensions "^1.0.0" + +aproba@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +arch@2.1.1, arch@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.1.tgz#8f5c2731aa35a30929221bb0640eed65175ec84e" + integrity sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8= + dependencies: + arr-flatten "^1.0.1" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.0.1, arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" + integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-flatten@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + +array-includes@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" + integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0" + is-string "^1.0.5" + +array-union@^1.0.1, array-union@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +array.prototype.flat@^1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" + integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + +asn1.js@^4.0.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assert@^1.1.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" + integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== + dependencies: + object-assign "^4.1.1" + util "0.10.3" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async-validator@~1.8.1: + version "1.8.5" + resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-1.8.5.tgz#dc3e08ec1fd0dddb67e60842f02c0cd1cec6d7f0" + integrity sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA== + dependencies: + babel-runtime "6.x" + +async@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" + integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ== + dependencies: + lodash "^4.17.10" + +async@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= + +async@^2.1.4, async@^2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +autoprefixer@^9.5.1: + version "9.7.3" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.7.3.tgz#fd42ed03f53de9beb4ca0d61fb4f7268a9bb50b4" + integrity sha512-8T5Y1C5Iyj6PgkPSFd0ODvK9DIleuPKUPYniNxybS47g2k2wFgLZ46lGQHlBuGKIAEV8fbCDfKCCRS1tvOgc3Q== + dependencies: + browserslist "^4.8.0" + caniuse-lite "^1.0.30001012" + chalk "^2.4.2" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^7.0.23" + postcss-value-parser "^4.0.2" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.0.tgz#24390e6ad61386b0a747265754d2a17219de862c" + integrity sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A== + +axios@^0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8" + integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ== + dependencies: + follow-redirects "1.5.10" + is-buffer "^2.0.2" + +babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-core@^6.0.0, babel-core@^6.26.0: + version "6.26.3" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207" + integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA== + dependencies: + babel-code-frame "^6.26.0" + babel-generator "^6.26.0" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + convert-source-map "^1.5.1" + debug "^2.6.9" + json5 "^0.5.1" + lodash "^4.17.4" + minimatch "^3.0.4" + path-is-absolute "^1.0.1" + private "^0.1.8" + slash "^1.0.0" + source-map "^0.5.7" + +babel-core@^7.0.0-bridge.0: + version "7.0.0-bridge.0" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" + integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== + +babel-eslint@^10.0.1, babel-eslint@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.3.tgz#81a2c669be0f205e19462fed2482d33e4687a88a" + integrity sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.0.0" + "@babel/traverse" "^7.0.0" + "@babel/types" "^7.0.0" + eslint-visitor-keys "^1.0.0" + resolve "^1.12.0" + +babel-extract-comments@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/babel-extract-comments/-/babel-extract-comments-1.0.0.tgz#0a2aedf81417ed391b85e18b4614e693a0351a21" + integrity sha512-qWWzi4TlddohA91bFwgt6zO/J0X+io7Qp184Fw0m2JYRSTZnJbFR8+07KmzudHCZgOiKRCrjhylwv9Xd8gfhVQ== + dependencies: + babylon "^6.18.0" + +babel-generator@^6.18.0, babel-generator@^6.26.0: + version "6.26.1" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" + integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA== + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.17.4" + source-map "^0.5.7" + trim-right "^1.0.1" + +babel-helper-vue-jsx-merge-props@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz#22aebd3b33902328e513293a8e4992b384f9f1b6" + integrity sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg== + +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + integrity sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI= + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-jest@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-23.6.0.tgz#a644232366557a2240a0c083da6b25786185a2f1" + integrity sha512-lqKGG6LYXYu+DQh/slrQ8nxXQkEkhugdXsU6St7GmhVS7Ilc/22ArwqXNJrf0QaOBjZB0360qZMwXqDYQHXaew== + dependencies: + babel-plugin-istanbul "^4.1.6" + babel-preset-jest "^23.2.0" + +babel-jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" + integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== + dependencies: + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/babel__core" "^7.1.0" + babel-plugin-istanbul "^5.1.0" + babel-preset-jest "^24.9.0" + chalk "^2.4.2" + slash "^2.0.0" + +babel-loader@^8.0.5: + version "8.0.6" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.6.tgz#e33bdb6f362b03f4bb141a0c21ab87c501b70dfb" + integrity sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw== + dependencies: + find-cache-dir "^2.0.0" + loader-utils "^1.0.2" + mkdirp "^0.5.1" + pify "^4.0.1" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-dynamic-import-node@^2.2.0, babel-plugin-dynamic-import-node@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" + integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== + dependencies: + object.assign "^4.1.0" + +babel-plugin-istanbul@^4.1.6: + version "4.1.6" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz#36c59b2192efce81c5b378321b74175add1c9a45" + integrity sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ== + dependencies: + babel-plugin-syntax-object-rest-spread "^6.13.0" + find-up "^2.1.0" + istanbul-lib-instrument "^1.10.1" + test-exclude "^4.2.1" + +babel-plugin-istanbul@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz#df4ade83d897a92df069c4d9a25cf2671293c854" + integrity sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + find-up "^3.0.0" + istanbul-lib-instrument "^3.3.0" + test-exclude "^5.2.3" + +babel-plugin-jest-hoist@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.2.0.tgz#e61fae05a1ca8801aadee57a6d66b8cefaf44167" + integrity sha1-5h+uBaHKiAGq3uV6bWa4zvr0QWc= + +babel-plugin-jest-hoist@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz#4f837091eb407e01447c8843cbec546d0002d756" + integrity sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw== + dependencies: + "@types/babel__traverse" "^7.0.6" + +babel-plugin-module-resolver@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-3.2.0.tgz#ddfa5e301e3b9aa12d852a9979f18b37881ff5a7" + integrity sha512-tjR0GvSndzPew/Iayf4uICWZqjBwnlMWjSx6brryfQ81F9rxBVqwDJtFCV8oOs0+vJeefK9TmdZtkIFdFe1UnA== + dependencies: + find-babel-config "^1.1.0" + glob "^7.1.2" + pkg-up "^2.0.0" + reselect "^3.0.1" + resolve "^1.4.0" + +babel-plugin-syntax-object-rest-spread@^6.13.0, babel-plugin-syntax-object-rest-spread@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" + integrity sha1-/WU28rzhODb/o6VFjEkDpZe7O/U= + +babel-plugin-transform-es2015-modules-commonjs@^6.26.0, babel-plugin-transform-es2015-modules-commonjs@^6.26.2: + version "6.26.2" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3" + integrity sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q== + dependencies: + babel-plugin-transform-strict-mode "^6.24.1" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-types "^6.26.0" + +babel-plugin-transform-object-rest-spread@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06" + integrity sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY= + dependencies: + babel-plugin-syntax-object-rest-spread "^6.8.0" + babel-runtime "^6.26.0" + +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + integrity sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-preset-jest@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz#8ec7a03a138f001a1a8fb1e8113652bf1a55da46" + integrity sha1-jsegOhOPABoaj7HoETZSvxpV2kY= + dependencies: + babel-plugin-jest-hoist "^23.2.0" + babel-plugin-syntax-object-rest-spread "^6.13.0" + +babel-preset-jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz#192b521e2217fb1d1f67cf73f70c336650ad3cdc" + integrity sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg== + dependencies: + "@babel/plugin-syntax-object-rest-spread" "^7.0.0" + babel-plugin-jest-hoist "^24.9.0" + +babel-register@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" + integrity sha1-btAhFz4vy0htestFxgCahW9kcHE= + dependencies: + babel-core "^6.26.0" + babel-runtime "^6.26.0" + core-js "^2.5.0" + home-or-tmp "^2.0.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + source-map-support "^0.4.15" + +babel-runtime@6.x, babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI= + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.0.0, babel-traverse@^6.18.0, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.0.0, babel-types@^6.18.0, babel-types@^6.24.1, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base64-js@^1.0.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +bfj@^6.1.1: + version "6.1.2" + resolved "https://registry.yarnpkg.com/bfj/-/bfj-6.1.2.tgz#325c861a822bcb358a41c78a33b8e6e2086dde7f" + integrity sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw== + dependencies: + bluebird "^3.5.5" + check-types "^8.0.3" + hoopy "^0.1.4" + tryer "^1.0.1" + +big.js@^3.1.3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" + integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q== + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +binary-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" + integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bluebird@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" + integrity sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw= + +bluebird@^3.1.1, bluebird@^3.5.1, bluebird@^3.5.5: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +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" + integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + +body-parser@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +bonjour@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" + integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= + dependencies: + array-flatten "^2.1.0" + deep-equal "^1.0.1" + dns-equal "^1.0.0" + dns-txt "^2.0.2" + multicast-dns "^6.0.1" + multicast-dns-service-types "^1.1.0" + +boolbase@^1.0.0, boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browser-process-hrtime@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4" + integrity sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw== + +browser-resolve@^1.11.3: + version "1.11.3" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" + integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== + dependencies: + resolve "1.1.7" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg= + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== + dependencies: + pako "~1.0.5" + +browserslist@^4.0.0, browserslist@^4.3.4, browserslist@^4.5.4, browserslist@^4.8.0: + version "4.8.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.3.tgz#65802fcd77177c878e015f0e3189f2c4f627ba44" + integrity sha512-iU43cMMknxG1ClEZ2MDKeonKE1CCrFVkQK2AqO2YWFmvIrx4JWrvQ4w4hQez6EpVI8rHTtqh/ruHHDHSOKxvUg== + dependencies: + caniuse-lite "^1.0.30001017" + electron-to-chromium "^1.3.322" + node-releases "^1.1.44" + +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + +buffer-from@1.x, buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer-indexof@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" + integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer@^4.3.0: + version "4.9.2" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" + integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= + +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" + integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +cacache@^10.0.4: + version "10.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460" + integrity sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA== + dependencies: + bluebird "^3.5.1" + chownr "^1.0.1" + glob "^7.1.2" + graceful-fs "^4.1.11" + lru-cache "^4.1.1" + mississippi "^2.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.2" + ssri "^5.2.4" + unique-filename "^1.1.0" + y18n "^4.0.0" + +cacache@^12.0.2: + version "12.0.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390" + integrity sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw== + dependencies: + bluebird "^3.5.5" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.4" + graceful-fs "^4.1.15" + infer-owner "^1.0.3" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.3" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cache-loader@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/cache-loader/-/cache-loader-2.0.1.tgz#5758f41a62d7c23941e3c3c7016e6faeb03acb07" + integrity sha512-V99T3FOynmGx26Zom+JrVBytLBsmUCzVG2/4NnUKgvXN4bEV42R1ERl1IyiH/cvFIDA1Ytq2lPZ9tXDSahcQpQ== + dependencies: + loader-utils "^1.1.0" + mkdirp "^0.5.1" + neo-async "^2.6.0" + normalize-path "^3.0.0" + schema-utils "^1.0.0" + +cachedir@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-1.3.0.tgz#5e01928bf2d95b5edd94b0942188246740e0dbc4" + integrity sha512-O1ji32oyON9laVPJL1IZ5bmwd2cB46VfpxkDequezH+15FDzzVddEyrGEeX4WusDSqKxdyFdDQDEG1yo1GoWkg== + dependencies: + os-homedir "^1.0.1" + +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" + integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= + +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + dependencies: + callsites "^2.0.0" + +caller-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" + integrity sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8= + dependencies: + callsites "^0.2.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + dependencies: + caller-callsite "^2.0.0" + +callsites@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" + integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo= + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@3.0.x: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= + +camelcase@^5.0.0, camelcase@^5.2.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001012, caniuse-lite@^1.0.30001017: + version "1.0.30001017" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001017.tgz#d3ad6ec18148b9bd991829958d9d7e562bb78cd6" + integrity sha512-EDnZyOJ6eYh6lHmCvCdHAFbfV4KJ9lSdfv4h/ppEhrU/Yudkl7jujwMZ1we6RX7DXqBfT04pVMQ4J+1wcTlsKA== + +capture-exit@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-1.2.0.tgz#1c5fcc489fd0ab00d4f1ac7ae1072e3173fbab6f" + integrity sha1-HF/MSJ/QqwDU8ax64QcuMXP7q28= + dependencies: + rsvp "^3.3.3" + +capture-exit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== + dependencies: + rsvp "^4.8.4" + +case-sensitive-paths-webpack-plugin@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.2.0.tgz#3371ef6365ef9c25fa4b81c16ace0e9c7dc58c3e" + integrity sha512-u5ElzokS8A1pm9vM3/iDgTcI3xqHxuCao94Oz8etI3cf0Tio0p8izkDYbTIn09uP3yUUr6+veaE6IkjnTYS46g== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" + integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I= + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +check-more-types@2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA= + +check-types@^8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552" + integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ== + +"chokidar@>=2.0.0 <4.0.0": + version "3.3.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.1.tgz#c84e5b3d18d9a4d77558fef466b1bf16bbeb3450" + integrity sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.3.0" + optionalDependencies: + fsevents "~2.1.2" + +chokidar@^2.0.2, chokidar@^2.0.4, chokidar@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chownr@^1.0.1, chownr@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" + integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== + +chrome-trace-event@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" + integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== + dependencies: + tslib "^1.9.0" + +ci-info@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" + integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +circular-json@^0.3.1: + version "0.3.3" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" + integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +clean-css@4.2.x: + version "4.2.1" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17" + integrity sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g== + dependencies: + source-map "~0.6.0" + +cli-cursor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= + dependencies: + restore-cursor "^1.0.1" + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-highlight@^2.1.0: + version "2.1.4" + resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-2.1.4.tgz#098cb642cf17f42adc1c1145e07f960ec4d7522b" + integrity sha512-s7Zofobm20qriqDoU9sXptQx0t2R9PEgac92mENNm7xaEe1hn71IIMsXMK+6encA6WRCWWxIGQbipr3q998tlQ== + dependencies: + chalk "^3.0.0" + highlight.js "^9.6.0" + mz "^2.4.0" + parse5 "^5.1.1" + parse5-htmlparser2-tree-adapter "^5.1.1" + yargs "^15.0.0" + +cli-spinners@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" + integrity sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw= + +cli-spinners@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.2.0.tgz#e8b988d9206c692302d8ee834e7a85c0144d8f77" + integrity sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ== + +cli-truncate@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" + integrity sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ= + dependencies: + slice-ansi "0.0.4" + string-width "^1.0.1" + +cli-width@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= + +clipboardy@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-2.1.0.tgz#0123a0c8fac92f256dc56335e0bb8be97a4909a5" + integrity sha512-2pzOUxWcLlXWtn+Jd6js3o12TysNOOVes/aQfg+MT/35vrxWzedHlLwyoJpXjsFKWm95BTNEcMGD9+a7mKzZkQ== + dependencies: + arch "^2.1.1" + execa "^1.0.0" + +cliui@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" + integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + wrap-ansi "^2.0.0" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +clone@2.x: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + +coa@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" + integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== + dependencies: + "@types/q" "^1.5.1" + chalk "^2.4.1" + q "^1.1.2" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0, color-convert@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" + integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10" + integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg== + dependencies: + color-convert "^1.9.1" + color-string "^1.5.2" + +colors@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@2.15.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== + +commander@2.17.x: + version "2.17.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" + integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== + +commander@^2.12.1, commander@^2.18.0, commander@^2.19.0, commander@^2.20.0, commander@~2.20.3: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@~2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" + integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== + +common-tags@1.8.0, common-tags@^1.4.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" + integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +compressible@~2.0.16: + version "2.0.17" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1" + integrity sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw== + dependencies: + mime-db ">= 1.40.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@1.6.2, concat-stream@^1.5.0, concat-stream@^1.6.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +condense-newlines@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/condense-newlines/-/condense-newlines-0.2.1.tgz#3de985553139475d32502c83b02f60684d24c55f" + integrity sha1-PemFVTE5R10yUCyDsC9gaE0kxV8= + dependencies: + extend-shallow "^2.0.1" + is-whitespace "^0.3.0" + kind-of "^3.0.2" + +config-chain@^1.1.12: + version "1.1.12" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" + integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + +connect-history-api-fallback@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" + integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== + +console-browserify@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" + integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== + +consolidate@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7" + integrity sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw== + dependencies: + bluebird "^3.1.1" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= + +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +convert-source-map@^1.4.0, convert-source-map@^1.5.1, convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +copy-concurrently@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== + dependencies: + aproba "^1.1.1" + fs-write-stream-atomic "^1.0.8" + iferr "^0.1.5" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.0" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +copy-webpack-plugin@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz#e7f40dd8a68477d405dd1b7a854aae324b158bae" + integrity sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA== + dependencies: + cacache "^10.0.4" + find-cache-dir "^1.0.0" + globby "^7.1.1" + is-glob "^4.0.0" + loader-utils "^1.1.0" + minimatch "^3.0.4" + p-limit "^1.0.0" + serialize-javascript "^1.4.0" + +core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.5: + version "2.6.11" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" + integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cosmiconfig@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +create-ecdh@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" + integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw== + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-spawn@^5.0.1, cross-spawn@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^6.0.0, cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" + integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +css-color-names@0.0.4, css-color-names@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= + +css-declaration-sorter@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" + integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== + dependencies: + postcss "^7.0.1" + timsort "^0.3.0" + +css-loader@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-1.0.1.tgz#6885bb5233b35ec47b006057da01cc640b6b79fe" + integrity sha512-+ZHAZm/yqvJ2kDtPne3uX0C+Vr3Zn5jFn2N4HywtS5ujwvsVkyg0VArEXpl3BgczDA8anieki1FIzhchX4yrDw== + dependencies: + babel-code-frame "^6.26.0" + css-selector-tokenizer "^0.7.0" + icss-utils "^2.1.0" + loader-utils "^1.0.2" + lodash "^4.17.11" + postcss "^6.0.23" + postcss-modules-extract-imports "^1.2.0" + postcss-modules-local-by-default "^1.2.0" + postcss-modules-scope "^1.1.0" + postcss-modules-values "^1.3.0" + postcss-value-parser "^3.3.0" + source-list-map "^2.0.0" + +css-select-base-adapter@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" + integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== + +css-select@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-select@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" + integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== + dependencies: + boolbase "^1.0.0" + css-what "^3.2.1" + domutils "^1.7.0" + nth-check "^1.0.2" + +css-selector-tokenizer@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz#a177271a8bca5019172f4f891fc6eed9cbf68d5d" + integrity sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA== + dependencies: + cssesc "^0.1.0" + fastparse "^1.1.1" + regexpu-core "^1.0.0" + +css-tree@1.0.0-alpha.37: + version "1.0.0-alpha.37" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" + integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== + dependencies: + mdn-data "2.0.4" + source-map "^0.6.1" + +css-unit-converter@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996" + integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY= + +css-what@2.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" + integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== + +css-what@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.2.1.tgz#f4a8f12421064621b456755e34a03a2c22df5da1" + integrity sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw== + +css@^2.1.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" + integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== + dependencies: + inherits "^2.0.3" + source-map "^0.6.1" + source-map-resolve "^0.5.2" + urix "^0.1.0" + +cssesc@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" + integrity sha1-yBSQPkViM3GgR3tAEJqq++6t27Q= + +cssesc@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" + integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg== + +cssnano-preset-default@^4.0.0, cssnano-preset-default@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76" + integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA== + dependencies: + css-declaration-sorter "^4.0.1" + cssnano-util-raw-cache "^4.0.1" + postcss "^7.0.0" + postcss-calc "^7.0.1" + postcss-colormin "^4.0.3" + postcss-convert-values "^4.0.1" + postcss-discard-comments "^4.0.2" + postcss-discard-duplicates "^4.0.2" + postcss-discard-empty "^4.0.1" + postcss-discard-overridden "^4.0.1" + postcss-merge-longhand "^4.0.11" + postcss-merge-rules "^4.0.3" + postcss-minify-font-values "^4.0.2" + postcss-minify-gradients "^4.0.2" + postcss-minify-params "^4.0.2" + postcss-minify-selectors "^4.0.2" + postcss-normalize-charset "^4.0.1" + postcss-normalize-display-values "^4.0.2" + postcss-normalize-positions "^4.0.2" + postcss-normalize-repeat-style "^4.0.2" + postcss-normalize-string "^4.0.2" + postcss-normalize-timing-functions "^4.0.2" + postcss-normalize-unicode "^4.0.1" + postcss-normalize-url "^4.0.1" + postcss-normalize-whitespace "^4.0.2" + postcss-ordered-values "^4.1.2" + postcss-reduce-initial "^4.0.3" + postcss-reduce-transforms "^4.0.2" + postcss-svgo "^4.0.2" + postcss-unique-selectors "^4.0.1" + +cssnano-util-get-arguments@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" + integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8= + +cssnano-util-get-match@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" + integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= + +cssnano-util-raw-cache@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" + integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== + dependencies: + postcss "^7.0.0" + +cssnano-util-same-parent@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" + integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== + +cssnano@^4.0.0, cssnano@^4.1.10: + version "4.1.10" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2" + integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== + dependencies: + cosmiconfig "^5.0.0" + cssnano-preset-default "^4.0.7" + is-resolvable "^1.0.0" + postcss "^7.0.0" + +csso@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.2.tgz#e5f81ab3a56b8eefb7f0092ce7279329f454de3d" + integrity sha512-kS7/oeNVXkHWxby5tHVxlhjizRCSv8QdU7hB2FpdAibDU8FjTAolhNjKNTiLzXtUrKT6HwClE81yXwEk1309wg== + dependencies: + css-tree "1.0.0-alpha.37" + +cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1" + integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== + dependencies: + cssom "0.3.x" + +current-script-polyfill@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/current-script-polyfill/-/current-script-polyfill-1.0.0.tgz#f31cf7e4f3e218b0726e738ca92a02d3488ef615" + integrity sha1-8xz35PPiGLBybnOMqSoC00iO9hU= + +cyclist@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" + integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= + +cypress@^3.2.0: + version "3.8.1" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-3.8.1.tgz#7821084e0ead81d35ffa29f2141c977dfdfc2343" + integrity sha512-eLk5OpL/ZMDfQx9t7ZaDUAGVcvSOPTi7CG1tiUnu9BGk7caBiDhuFi3Tz/D5vWqH/Dl6Uh4X+Au4W+zh0xzbXw== + dependencies: + "@cypress/listr-verbose-renderer" "0.4.1" + "@cypress/xvfb" "1.2.4" + "@types/sizzle" "2.3.2" + arch "2.1.1" + bluebird "3.5.0" + cachedir "1.3.0" + chalk "2.4.2" + check-more-types "2.24.0" + commander "2.15.1" + common-tags "1.8.0" + debug "3.2.6" + execa "0.10.0" + executable "4.1.1" + extract-zip "1.6.7" + fs-extra "5.0.0" + getos "3.1.1" + is-ci "1.2.1" + is-installed-globally "0.1.0" + lazy-ass "1.6.0" + listr "0.12.0" + lodash "4.17.15" + log-symbols "2.2.0" + minimist "1.2.0" + moment "2.24.0" + ramda "0.24.1" + request "2.88.0" + request-progress "3.0.0" + supports-color "5.5.0" + tmp "0.1.0" + untildify "3.0.3" + url "0.11.0" + yauzl "2.10.0" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +data-urls@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" + integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== + dependencies: + abab "^2.0.0" + whatwg-mimetype "^2.2.0" + whatwg-url "^7.0.0" + +date-fns@^1.27.2: + version "1.30.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" + integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== + +de-indent@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" + integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0= + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@3.2.6, debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@=3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +decamelize@^1.1.1, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +deep-equal@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" + integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== + dependencies: + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +deepmerge@^1.2.0, deepmerge@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753" + integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ== + +default-gateway@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" + integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== + dependencies: + execa "^1.0.0" + ip-regex "^2.1.0" + +default-gateway@^5.0.2: + version "5.0.5" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-5.0.5.tgz#4fd6bd5d2855d39b34cc5a59505486e9aafc9b10" + integrity sha512-z2RnruVmj8hVMmAnEJMTIJNijhKCDiGjbLP+BHJFOT7ld3Bo5qcIBpVYDniqhbMIIf+jZDlkP2MkPXiQy/DBLA== + dependencies: + execa "^3.3.0" + +default-require-extensions@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" + integrity sha1-836hXT4T/9m0N9M+GnW1+5eHTLg= + dependencies: + strip-bom "^2.0.0" + +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + dependencies: + clone "^1.0.2" + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +del@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" + integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== + dependencies: + "@types/glob" "^7.1.1" + globby "^6.1.0" + is-path-cwd "^2.0.0" + is-path-in-cwd "^2.0.0" + p-map "^2.0.0" + pify "^4.0.1" + rimraf "^2.6.3" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg= + dependencies: + repeating "^2.0.0" + +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + +detect-newline@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" + integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= + +detect-node@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" + integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== + +diff-sequences@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" + integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== + +diff@^3.2.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + +diff@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.1.tgz#0c667cb467ebbb5cea7f14f135cc2dba7780a8ff" + integrity sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q== + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-glob@^2.0.0, dir-glob@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" + integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== + dependencies: + path-type "^3.0.0" + +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= + +dns-packet@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" + integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== + dependencies: + ip "^1.1.0" + safe-buffer "^5.0.1" + +dns-txt@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" + integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= + dependencies: + buffer-indexof "^1.0.0" + +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-converter@^0.2: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== + dependencies: + utila "~0.4" + +dom-event-types@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dom-event-types/-/dom-event-types-1.0.0.tgz#5830a0a29e1bf837fe50a70cd80a597232813cae" + integrity sha512-2G2Vwi2zXTHBGqXHsJ4+ak/iP0N8Ar+G8a7LiD2oup5o4sQWytwqqrZu/O6hIMV0KMID2PL69OhpshLO0n7UJQ== + +dom-serializer@0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + +domain-browser@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== + +domelementtype@1, domelementtype@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== + +domelementtype@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" + integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== + +domexception@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" + integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== + dependencies: + webidl-conversions "^4.0.2" + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== + dependencies: + domelementtype "1" + +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1, domutils@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + +dot-prop@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" + integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ== + dependencies: + is-obj "^1.0.0" + +dotenv-expand@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" + integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== + +dotenv@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-7.0.0.tgz#a2be3cd52736673206e8a85fb5210eea29628e7c" + integrity sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g== + +duplexer@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= + +duplexify@^3.4.2, duplexify@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +easy-stack@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/easy-stack/-/easy-stack-1.0.0.tgz#12c91b3085a37f0baa336e9486eac4bf94e3e788" + integrity sha1-EskbMIWjfwuqM26UhurEv5Tj54g= + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +editorconfig@^0.15.3: + version "0.15.3" + resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5" + integrity sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g== + dependencies: + commander "^2.19.0" + lru-cache "^4.1.5" + semver "^5.6.0" + sigmund "^1.0.1" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +ejs@^2.6.1: + version "2.7.4" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" + integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== + +electron-to-chromium@^1.3.322: + version "1.3.322" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz#a6f7e1c79025c2b05838e8e344f6e89eb83213a8" + integrity sha512-Tc8JQEfGQ1MzfSzI/bTlSr7btJv/FFO7Yh6tanqVmIWOuNCu6/D1MilIEgLtmWqIrsv+o4IjpLAhgMBr/ncNAA== + +elegant-spinner@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" + integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= + +element-ui@^2.12.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/element-ui/-/element-ui-2.13.0.tgz#f6bb04e5b0a76ea5f62466044b774407ba4ebd2d" + integrity sha512-KYsHWsBXYbLELS8cdfvgJTOMSUby3UEjvsPV1V1VmgJ/DdkOAS4z3MiOrPxrT9w2Cc5lZ4eVSQiGhYFR5NVChw== + dependencies: + async-validator "~1.8.1" + babel-helper-vue-jsx-merge-props "^2.0.0" + deepmerge "^1.2.0" + normalize-wheel "^1.0.1" + resize-observer-polyfill "^1.5.0" + throttle-debounce "^1.0.1" + +elliptic@^6.0.0: + version "6.5.2" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762" + integrity sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66" + integrity sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA== + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.5.0" + tapable "^1.0.0" + +entities@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== + +entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" + integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== + +errno@^0.1.3, errno@~0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" + integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== + dependencies: + prr "~1.0.1" + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +error-stack-parser@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.4.tgz#a757397dc5d9de973ac9a5d7d4e8ade7cfae9101" + integrity sha512-fZ0KkoxSjLFmhW5lHbUT3tLwy3nX1qEzMYo8koY1vrsAco53CMT1djnBSeC/wUjTEZRhZl9iRw7PaMaxfJ4wzQ== + dependencies: + stackframe "^1.1.0" + +es-abstract@^1.17.0, es-abstract@^1.17.0-next.1: + version "1.17.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.0.tgz#f42a517d0036a5591dbb2c463591dc8bb50309b1" + integrity sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.1.5" + is-regex "^1.0.5" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimleft "^2.1.1" + string.prototype.trimright "^2.1.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escodegen@^1.9.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.12.1.tgz#08770602a74ac34c7a90ca9229e7d51e379abc76" + integrity sha512-Q8t2YZ+0e0pc7NRVj3B4tSQ9rim1oi4Fh46k2xhJ2qOiEwhQfdjyEQddWdj7ZFaKmU+5104vn1qrcjEPWq+bgQ== + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +eslint-config-standard@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-12.0.0.tgz#638b4c65db0bd5a41319f96bba1f15ddad2107d9" + integrity sha512-COUz8FnXhqFitYj4DTqHzidjIL/t4mumGZto5c7DrBpvWoie+Sn3P4sLEzUGeYhRElWuFEf8K1S1EfvD1vixCQ== + +eslint-import-resolver-node@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" + integrity sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q== + dependencies: + debug "^2.6.9" + resolve "^1.5.0" + +eslint-loader@^2.1.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-2.2.1.tgz#28b9c12da54057af0845e2a6112701a2f6bf8337" + integrity sha512-RLgV9hoCVsMLvOxCuNjdqOrUqIj9oJg8hF44vzJaYqsAHuY9G2YAeN3joQ9nxP0p5Th9iFSIpKo+SD8KISxXRg== + dependencies: + loader-fs-cache "^1.0.0" + loader-utils "^1.0.2" + object-assign "^4.0.1" + object-hash "^1.1.4" + rimraf "^2.6.1" + +eslint-module-utils@^2.4.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.5.0.tgz#cdf0b40d623032274ccd2abd7e64c4e524d6e19c" + integrity sha512-kCo8pZaNz2dsAW7nCUjuVoI11EBXXpIzfNxmaoLhXoRDOnqXLC4iSGVRdZPhOitfbdEfMEfKOiENaK6wDPZEGw== + dependencies: + debug "^2.6.9" + pkg-dir "^2.0.0" + +eslint-plugin-cypress@^2.2.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-cypress/-/eslint-plugin-cypress-2.8.1.tgz#981a0f3658b40de430bcf05cabc96b396487c91f" + integrity sha512-jDpcP+MmjmqQO/x3bwIXgp4cl7Q66RYS5/IsuOQP4Qo2sEqE3DI8tTxBQ1EhnV5qEDd2Z2TYHR+5vYI6oCN4uw== + dependencies: + globals "^11.12.0" + +eslint-plugin-es@^1.3.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-1.4.1.tgz#12acae0f4953e76ba444bfd1b2271081ac620998" + integrity sha512-5fa/gR2yR3NxQf+UXkeLeP8FBBl6tSgdrAz1+cF84v1FMM4twGwQoqTnn+QxFLcPOrF4pdKEJKDB/q9GoyJrCA== + dependencies: + eslint-utils "^1.4.2" + regexpp "^2.0.1" + +eslint-plugin-import@^2.14.0: + version "2.19.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.19.1.tgz#5654e10b7839d064dd0d46cd1b88ec2133a11448" + integrity sha512-x68131aKoCZlCae7rDXKSAQmbT5DQuManyXo2sK6fJJ0aK5CWAkv6A6HJZGgqC8IhjQxYPgo6/IY4Oz8AFsbBw== + dependencies: + array-includes "^3.0.3" + array.prototype.flat "^1.2.1" + contains-path "^0.1.0" + debug "^2.6.9" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.2" + eslint-module-utils "^2.4.1" + has "^1.0.3" + minimatch "^3.0.4" + object.values "^1.1.0" + read-pkg-up "^2.0.0" + resolve "^1.12.0" + +eslint-plugin-node@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-8.0.1.tgz#55ae3560022863d141fa7a11799532340a685964" + integrity sha512-ZjOjbjEi6jd82rIpFSgagv4CHWzG9xsQAVp1ZPlhRnnYxcTgENUVBvhYmkQ7GvT1QFijUSo69RaiOJKhMu6i8w== + dependencies: + eslint-plugin-es "^1.3.1" + eslint-utils "^1.3.1" + ignore "^5.0.2" + minimatch "^3.0.4" + resolve "^1.8.1" + semver "^5.5.0" + +eslint-plugin-promise@^4.0.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" + integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== + +eslint-plugin-standard@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz#ff0519f7ffaff114f76d1bd7c3996eef0f6e20b4" + integrity sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ== + +eslint-plugin-vue@^4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-4.7.1.tgz#c829b9fc62582c1897b5a0b94afd44ecca511e63" + integrity sha512-esETKhVMI7Vdli70Wt4bvAwnZBJeM0pxVX9Yb0wWKxdCJc2EADalVYK/q2FzMw8oKN0wPMdqVCKS8kmR89recA== + dependencies: + vue-eslint-parser "^2.0.3" + +eslint-plugin-vue@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-5.2.3.tgz#3ee7597d823b5478804b2feba9863b1b74273961" + integrity sha512-mGwMqbbJf0+VvpGR5Lllq0PMxvTdrZ/ZPjmhkacrCHbubJeJOt+T6E3HUzAifa2Mxi7RSdJfC9HFpOeSYVMMIw== + dependencies: + vue-eslint-parser "^5.0.0" + +eslint-scope@^3.7.1: + version "3.7.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535" + integrity sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-scope@^4.0.0, eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-scope@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" + integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.3.1, eslint-utils@^1.4.2, 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== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" + integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== + +eslint@^4.19.1: + version "4.19.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" + integrity sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ== + dependencies: + ajv "^5.3.0" + babel-code-frame "^6.22.0" + chalk "^2.1.0" + concat-stream "^1.6.0" + cross-spawn "^5.1.0" + debug "^3.1.0" + doctrine "^2.1.0" + eslint-scope "^3.7.1" + eslint-visitor-keys "^1.0.0" + espree "^3.5.4" + esquery "^1.0.0" + esutils "^2.0.2" + file-entry-cache "^2.0.0" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.0.1" + ignore "^3.3.3" + imurmurhash "^0.1.4" + inquirer "^3.0.6" + is-resolvable "^1.0.0" + js-yaml "^3.9.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.4" + minimatch "^3.0.2" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + pluralize "^7.0.0" + progress "^2.0.0" + regexpp "^1.0.1" + require-uncached "^1.0.3" + semver "^5.3.0" + strip-ansi "^4.0.0" + strip-json-comments "~2.0.1" + table "4.0.2" + text-table "~0.2.0" + +eslint@^6.2.2: + version "6.8.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" + integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.10.0" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^5.0.0" + eslint-utils "^1.4.3" + eslint-visitor-keys "^1.1.0" + espree "^6.1.2" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^7.0.0" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.14" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.3" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^6.1.2" + strip-ansi "^5.2.0" + strip-json-comments "^3.0.1" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^3.5.2, espree@^3.5.4: + version "3.5.4" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" + integrity sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A== + dependencies: + acorn "^5.5.0" + acorn-jsx "^3.0.0" + +espree@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-4.1.0.tgz#728d5451e0fd156c04384a7ad89ed51ff54eb25f" + integrity sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w== + dependencies: + acorn "^6.0.2" + acorn-jsx "^5.0.0" + eslint-visitor-keys "^1.0.0" + +espree@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d" + integrity sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA== + dependencies: + acorn "^7.1.0" + acorn-jsx "^5.1.0" + eslint-visitor-keys "^1.1.0" + +esprima@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.0.0, esquery@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" + integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== + dependencies: + estraverse "^4.0.0" + +esrecurse@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== + dependencies: + estraverse "^4.1.0" + +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +event-pubsub@4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/event-pubsub/-/event-pubsub-4.3.0.tgz#f68d816bc29f1ec02c539dc58c8dd40ce72cb36e" + integrity sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ== + +eventemitter3@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb" + integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg== + +events@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88" + integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA== + +eventsource@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" + integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ== + dependencies: + original "^1.0.0" + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +exec-sh@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.2.tgz#2a5e7ffcbd7d0ba2755bdecb16e5a427dfbdec36" + integrity sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw== + dependencies: + merge "^1.2.0" + +exec-sh@^0.3.2: + version "0.3.4" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5" + integrity sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A== + +execa@0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" + integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw== + dependencies: + cross-spawn "^6.0.0" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" + integrity sha1-2NdrvBtVIX7RkP1t1J08d07PyNo= + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^3.3.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-3.4.0.tgz#c08ed4550ef65d858fac269ffc8572446f37eb89" + integrity sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + p-finally "^2.0.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +executable@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" + integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== + dependencies: + pify "^2.2.0" + +exit-hook@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" + integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s= + dependencies: + is-posix-bracket "^0.1.0" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= + dependencies: + fill-range "^2.1.0" + +expect@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-23.6.0.tgz#1e0c8d3ba9a581c87bd71fb9bc8862d443425f98" + integrity sha512-dgSoOHgmtn/aDGRVFWclQyPDKl2CQRq0hmIEoUAuQs/2rn2NcvCWcSCovm6BLeuB/7EZuLGu2QfnR+qRt5OM4w== + dependencies: + ansi-styles "^3.2.0" + jest-diff "^23.6.0" + jest-get-type "^22.1.0" + jest-matcher-utils "^23.6.0" + jest-message-util "^23.4.0" + jest-regex-util "^23.3.0" + +expect@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" + integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q== + dependencies: + "@jest/types" "^24.9.0" + ansi-styles "^3.2.0" + jest-get-type "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-regex-util "^24.9.0" + +express@^4.16.3, express@^4.17.1: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^2.0.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" + integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A== + dependencies: + chardet "^0.4.0" + iconv-lite "^0.4.17" + tmp "^0.0.33" + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE= + dependencies: + is-extglob "^1.0.0" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extract-from-css@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/extract-from-css/-/extract-from-css-0.4.4.tgz#1ea7df2e7c7c6eb9922fa08e8adaea486f6f8f92" + integrity sha1-HqffLnx8brmSL6COitrqSG9vj5I= + dependencies: + css "^2.1.0" + +extract-zip@1.6.7: + version "1.6.7" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9" + integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k= + dependencies: + concat-stream "1.6.2" + debug "2.6.9" + mkdirp "0.5.1" + yauzl "2.4.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" + integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= + +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= + +fast-glob@^2.2.6: + version "2.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fastparse@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" + integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== + +faye-websocket@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= + dependencies: + websocket-driver ">=0.5.1" + +faye-websocket@~0.11.1: + version "0.11.3" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" + integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA== + dependencies: + websocket-driver ">=0.5.1" + +fb-watchman@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + dependencies: + bser "2.1.1" + +fd-slicer@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" + integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU= + dependencies: + pend "~1.2.0" + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + +fibers@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/fibers/-/fibers-4.0.2.tgz#d04f9ccd0aba179588202202faeb4fed65d497f5" + integrity sha512-FhICi1K4WZh9D6NC18fh2ODF3EWy1z0gzIdV9P7+s2pRjfRBnCkMDJ6x3bV1DkVymKH8HGrQa/FNOBjYvnJ/tQ== + dependencies: + detect-libc "^1.0.3" + +figgy-pudding@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" + integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== + +figures@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + dependencies: + escape-string-regexp "^1.0.5" + +figures@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.1.0.tgz#4b198dd07d8d71530642864af2d45dd9e459c4ec" + integrity sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" + integrity sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E= + dependencies: + flat-cache "^1.2.1" + object-assign "^4.0.1" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +file-loader@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-3.0.1.tgz#f8e0ba0b599918b51adfe45d66d1e771ad560faa" + integrity sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw== + dependencies: + loader-utils "^1.0.2" + schema-utils "^1.0.0" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +filename-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= + +fileset@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0" + integrity sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA= + dependencies: + glob "^7.0.3" + minimatch "^3.0.3" + +filesize@^3.6.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" + integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== + +fill-range@^2.1.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" + integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q== + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^3.0.0" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-babel-config@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-1.2.0.tgz#a9b7b317eb5b9860cda9d54740a8c8337a2283a2" + integrity sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA== + dependencies: + json5 "^0.5.1" + path-exists "^3.0.0" + +find-cache-dir@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" + integrity sha1-yN765XyKUqinhPnjHFfHQumToLk= + dependencies: + commondir "^1.0.1" + mkdirp "^0.5.1" + pkg-dir "^1.0.0" + +find-cache-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" + integrity sha1-kojj6ePMN0hxfTnq3hfPcfww7m8= + dependencies: + commondir "^1.0.1" + make-dir "^1.0.0" + pkg-dir "^2.0.0" + +find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flat-cache@^1.2.1: + version "1.3.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.4.tgz#2c2ef77525cc2929007dfffa1dd314aa9c9dee6f" + integrity sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg== + dependencies: + circular-json "^0.3.1" + graceful-fs "^4.1.2" + rimraf "~2.6.2" + write "^0.2.1" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" + integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== + +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" + integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== + dependencies: + inherits "^2.0.3" + readable-stream "^2.3.6" + +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + +follow-redirects@^1.0.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.9.0.tgz#8d5bcdc65b7108fe1508649c79c12d732dcedb4f" + integrity sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A== + dependencies: + debug "^3.0.0" + +for-in@^1.0.1, for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= + dependencies: + for-in "^1.0.1" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +fork-ts-checker-webpack-plugin@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-0.5.2.tgz#a73b3630bd0a69409a6e4824e54c03a62fe82d8f" + integrity sha512-a5IG+xXyKnpruI0CP/anyRLAoxWtp3lzdG6flxicANnoSzz64b12dJ7ASAVRrI2OaWwZR2JyBaMHFQqInhWhIw== + dependencies: + babel-code-frame "^6.22.0" + chalk "^2.4.1" + chokidar "^2.0.4" + micromatch "^3.1.10" + minimatch "^3.0.4" + tapable "^1.0.0" + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +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= + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs-extra@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" + integrity sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^7.0.1: + 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-plus@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fs-plus/-/fs-plus-3.1.1.tgz#02c085ba0a013084cff2f3e89b17c60c1d9b4ab5" + integrity sha512-Se2PJdOWXqos1qVTkvqqjb0CSnfBnwwD+pq+z4ksT+e97mEShod/hrNg0TRCCsXPbJzcIq+NuzQhigunMWMJUA== + dependencies: + async "^1.5.2" + mkdirp "^0.5.1" + rimraf "^2.5.2" + underscore-plus "1.x" + +fs-write-stream-atomic@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= + dependencies: + graceful-fs "^4.1.2" + iferr "^0.1.5" + imurmurhash "^0.1.4" + readable-stream "1 || 2" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^1.2.3, fsevents@^1.2.7: + version "1.2.11" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.11.tgz#67bf57f4758f02ede88fb2a1712fef4d15358be3" + integrity sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + +fsevents@~2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" + integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== + +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= + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9" + integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw== + dependencies: + pump "^3.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +getos@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/getos/-/getos-3.1.1.tgz#967a813cceafee0156b0483f7cffa5b3eff029c5" + integrity sha512-oUP1rnEhAr97rkitiszGP9EgDVYnmchgFzfqRzSkgtfv7ai6tEi7Ko8GgjNXts7VLWEqrTWyhsOKLe5C5b/Zkg== + dependencies: + async "2.6.1" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= + dependencies: + is-glob "^2.0.0" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@^5.0.0, glob-parent@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" + integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== + dependencies: + is-glob "^4.0.1" + +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= + +glob@^7.0.3, glob@^7.1.1, 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== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-dirs@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" + integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU= + dependencies: + ini "^1.3.4" + +globals@^11.0.1, globals@^11.1.0, globals@^11.12.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^12.1.0: + version "12.3.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.3.0.tgz#1e564ee5c4dded2ab098b0f88f24702a3c56be13" + integrity sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw== + dependencies: + type-fest "^0.8.1" + +globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== + +globby@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" + integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw= + dependencies: + array-union "^1.0.1" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +globby@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" + integrity sha1-+yzP+UAfhgCUXfral0QMypcrhoA= + dependencies: + array-union "^1.0.1" + dir-glob "^2.0.0" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + +globby@^9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" + integrity sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg== + dependencies: + "@types/glob" "^7.1.1" + array-union "^1.0.2" + dir-glob "^2.2.2" + fast-glob "^2.2.6" + glob "^7.1.3" + ignore "^4.0.3" + pify "^4.0.1" + slash "^2.0.0" + +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.2.3" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" + integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== + +growly@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= + +gzip-size@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" + integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== + dependencies: + duplexer "^0.1.1" + pify "^4.0.1" + +handle-thing@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" + integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== + +handlebars@^4.0.3, handlebars@^4.1.2, handlebars@^4.5.3: + version "4.5.3" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482" + integrity sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA== + dependencies: + neo-async "^2.6.0" + optimist "^0.6.1" + source-map "^0.6.1" + optionalDependencies: + uglify-js "^3.1.4" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" + integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== + dependencies: + ajv "^6.5.5" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +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-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.0, has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +hash-sum@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04" + integrity sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ= + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +he@1.2.x, he@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hex-color-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" + integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== + +highlight.js@^9.6.0: + version "9.17.1" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.17.1.tgz#14a4eded23fd314b05886758bb906e39dd627f9a" + integrity sha512-TA2/doAur5Ol8+iM3Ov7qy3jYcr/QiJ2eDTdRF4dfbjG7AaaB99J5G+zSl11ljbl6cIcahgPY6SKb3sC3EJ0fw== + dependencies: + handlebars "^4.5.3" + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hoek@4.x.x: + version "4.2.1" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" + integrity sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA== + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + integrity sha1-42w/LSyufXRqhX440Y1fMqeILbg= + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +hoopy@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" + integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ== + +hosted-git-info@^2.1.4: + version "2.8.5" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" + integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +hsl-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" + integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= + +hsla-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" + integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= + +html-comment-regex@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" + integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== + +html-encoding-sniffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" + integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== + dependencies: + whatwg-encoding "^1.0.1" + +html-entities@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" + integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= + +html-minifier@^3.2.3: + version "3.5.21" + resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c" + integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA== + dependencies: + camel-case "3.0.x" + clean-css "4.2.x" + commander "2.17.x" + he "1.2.x" + param-case "2.1.x" + relateurl "0.2.x" + uglify-js "3.4.x" + +html-tags@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" + integrity sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos= + +html-webpack-plugin@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz#b01abbd723acaaa7b37b6af4492ebda03d9dd37b" + integrity sha1-sBq71yOsqqeze2r0SS69oD2d03s= + dependencies: + html-minifier "^3.2.3" + loader-utils "^0.2.16" + lodash "^4.17.3" + pretty-error "^2.0.2" + tapable "^1.0.0" + toposort "^1.0.0" + util.promisify "1.0.0" + +htmlparser2@^3.3.0: + version "3.10.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" + integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== + dependencies: + domelementtype "^1.3.1" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.1.1" + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +"http-parser-js@>=0.4.0 <0.4.11": + version "0.4.10" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4" + integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q= + +http-proxy-middleware@0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" + integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== + dependencies: + http-proxy "^1.17.0" + is-glob "^4.0.0" + lodash "^4.17.11" + micromatch "^3.1.10" + +http-proxy@^1.17.0: + version "1.18.0" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a" + integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= + +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + +iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +icss-replace-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" + integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= + +icss-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.1.0.tgz#83f0a0ec378bf3246178b6c2ad9136f135b1c962" + integrity sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI= + dependencies: + postcss "^6.0.1" + +ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + +iferr@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= + +ignore@^3.3.3, ignore@^3.3.5: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== + +ignore@^4.0.3, ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +ignore@^5.0.2: + version "5.1.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf" + integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A== + +import-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" + integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk= + dependencies: + import-from "^2.1.0" + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +import-fresh@^3.0.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" + integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-from@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" + integrity sha1-M1238qev/VOqpHHUuAId7ja387E= + dependencies: + resolve-from "^3.0.0" + +import-local@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc" + integrity sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ== + dependencies: + pkg-dir "^2.0.0" + resolve-cwd "^2.0.0" + +import-local@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" + integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== + dependencies: + pkg-dir "^3.0.0" + resolve-cwd "^2.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= + dependencies: + repeating "^2.0.0" + +indent-string@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" + integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= + +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= + +infer-owner@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ini@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + +inquirer@^3.0.6: + version "3.3.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" + integrity sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ== + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^2.0.4" + figures "^2.0.0" + lodash "^4.3.0" + mute-stream "0.0.7" + run-async "^2.2.0" + rx-lite "^4.0.8" + rx-lite-aggregates "^4.0.8" + string-width "^2.1.0" + strip-ansi "^4.0.0" + through "^2.3.6" + +inquirer@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.1.tgz#13f7980eedc73c689feff3994b109c4e799c6ebb" + integrity sha512-V1FFQ3TIO15det8PijPLFR9M9baSlnRs9nL7zWu1MNVA2T9YVl9ZbrHJhYs7e9X8jeMZ3lr2JH/rdHFgNCBdYw== + dependencies: + ansi-escapes "^4.2.1" + chalk "^2.4.2" + cli-cursor "^3.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.15" + mute-stream "0.0.8" + run-async "^2.2.0" + rxjs "^6.5.3" + string-width "^4.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +internal-ip@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" + integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== + dependencies: + default-gateway "^4.2.0" + ipaddr.js "^1.9.0" + +invariant@^2.2.2, invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +invert-kv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" + integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== + +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= + +ip@^1.1.0, ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + +ipaddr.js@1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" + integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== + +ipaddr.js@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-absolute-url@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" + integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= + +is-absolute-url@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arguments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" + integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +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== + +is-buffer@^2.0.2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" + integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== + +is-callable@^1.1.4, is-callable@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" + integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== + +is-ci@1.2.1, is-ci@^1.0.10: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c" + integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg== + dependencies: + ci-info "^1.5.0" + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-color-stop@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" + integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= + dependencies: + css-color-names "^0.0.4" + hex-color-regex "^1.1.0" + hsl-regex "^1.0.0" + hsla-regex "^1.0.0" + rgb-regex "^1.0.1" + rgba-regex "^1.0.0" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= + +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ= + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-fn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-1.0.0.tgz#969d49e1bb3329f6bb7f09089be26578b2ddd46a" + integrity sha1-lp1J4bszKfa7fwkIm+JleLLd1Go= + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= + dependencies: + is-extglob "^1.0.0" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-installed-globally@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" + integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA= + dependencies: + global-dirs "^0.1.0" + is-path-inside "^1.0.0" + +is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= + dependencies: + kind-of "^3.0.2" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-obj@^1.0.0, is-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= + +is-path-cwd@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-in-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" + integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== + dependencies: + is-path-inside "^2.1.0" + +is-path-inside@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" + integrity sha1-jvW33lBDej/cprToZe96pVy0gDY= + dependencies: + path-is-inside "^1.0.1" + +is-path-inside@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" + integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== + dependencies: + path-is-inside "^1.0.2" + +is-plain-obj@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q= + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= + +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= + +is-regex@^1.0.4, is-regex@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" + integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== + dependencies: + has "^1.0.3" + +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= + +is-resolvable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" + integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + +is-svg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" + integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ== + dependencies: + html-comment-regex "^1.1.0" + +is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + +is-whitespace@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/is-whitespace/-/is-whitespace-0.3.0.tgz#1639ecb1be036aec69a54cbb401cfbed7114ab7f" + integrity sha1-Fjnssb4DauxppUy7QBz77XEUq38= + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isemail@3.x.x: + version "3.2.0" + resolved "https://registry.yarnpkg.com/isemail/-/isemail-3.2.0.tgz#59310a021931a9fb06bbb51e155ce0b3f236832c" + integrity sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg== + dependencies: + punycode "2.x.x" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +istanbul-api@^1.3.1: + version "1.3.7" + resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.3.7.tgz#a86c770d2b03e11e3f778cd7aedd82d2722092aa" + integrity sha512-4/ApBnMVeEPG3EkSzcw25wDe4N66wxwn+KKn6b47vyek8Xb3NBAcg4xfuQbS7BqcZuTX4wxfD5lVagdggR3gyA== + dependencies: + async "^2.1.4" + fileset "^2.0.2" + istanbul-lib-coverage "^1.2.1" + istanbul-lib-hook "^1.2.2" + istanbul-lib-instrument "^1.10.2" + istanbul-lib-report "^1.1.5" + istanbul-lib-source-maps "^1.2.6" + istanbul-reports "^1.5.1" + js-yaml "^3.7.0" + mkdirp "^0.5.1" + once "^1.4.0" + +istanbul-lib-coverage@^1.2.0, istanbul-lib-coverage@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz#ccf7edcd0a0bb9b8f729feeb0930470f9af664f0" + integrity sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ== + +istanbul-lib-coverage@^2.0.2, istanbul-lib-coverage@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" + integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== + +istanbul-lib-hook@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.2.2.tgz#bc6bf07f12a641fbf1c85391d0daa8f0aea6bf86" + integrity sha512-/Jmq7Y1VeHnZEQ3TL10VHyb564mn6VrQXHchON9Jf/AEcmQ3ZIiyD1BVzNOKTZf/G3gE+kiGK6SmpF9y3qGPLw== + dependencies: + append-transform "^0.4.0" + +istanbul-lib-instrument@^1.10.1, istanbul-lib-instrument@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz#1f55ed10ac3c47f2bdddd5307935126754d0a9ca" + integrity sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A== + dependencies: + babel-generator "^6.18.0" + babel-template "^6.16.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + babylon "^6.18.0" + istanbul-lib-coverage "^1.2.1" + semver "^5.3.0" + +istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" + integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== + dependencies: + "@babel/generator" "^7.4.0" + "@babel/parser" "^7.4.3" + "@babel/template" "^7.4.0" + "@babel/traverse" "^7.4.3" + "@babel/types" "^7.4.0" + istanbul-lib-coverage "^2.0.5" + semver "^6.0.0" + +istanbul-lib-report@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz#f2a657fc6282f96170aaf281eb30a458f7f4170c" + integrity sha512-UsYfRMoi6QO/doUshYNqcKJqVmFe9w51GZz8BS3WB0lYxAllQYklka2wP9+dGZeHYaWIdcXUx8JGdbqaoXRXzw== + dependencies: + istanbul-lib-coverage "^1.2.1" + mkdirp "^0.5.1" + path-parse "^1.0.5" + supports-color "^3.1.2" + +istanbul-lib-report@^2.0.4: + version "2.0.8" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" + integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ== + dependencies: + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + supports-color "^6.1.0" + +istanbul-lib-source-maps@^1.2.4, istanbul-lib-source-maps@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.6.tgz#37b9ff661580f8fca11232752ee42e08c6675d8f" + integrity sha512-TtbsY5GIHgbMsMiRw35YBHGpZ1DVFEO19vxxeiDMYaeOFOCzfnYVxvl6pOUIZR4dtPhAGpSMup8OyF8ubsaqEg== + dependencies: + debug "^3.1.0" + istanbul-lib-coverage "^1.2.1" + mkdirp "^0.5.1" + rimraf "^2.6.1" + source-map "^0.5.3" + +istanbul-lib-source-maps@^3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" + integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + rimraf "^2.6.3" + source-map "^0.6.1" + +istanbul-reports@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.5.1.tgz#97e4dbf3b515e8c484caea15d6524eebd3ff4e1a" + integrity sha512-+cfoZ0UXzWjhAdzosCPP3AN8vvef8XDkWtTfgaN+7L3YTpNYITnCaEkceo5SEYy644VkHka/P1FvkWvrG/rrJw== + dependencies: + handlebars "^4.0.3" + +istanbul-reports@^2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.6.tgz#7b4f2660d82b29303a8fe6091f8ca4bf058da1af" + integrity sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA== + dependencies: + handlebars "^4.1.2" + +javascript-stringify@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-1.6.0.tgz#142d111f3a6e3dae8f4a9afd77d45855b5a9cce3" + integrity sha1-FC0RHzpuPa6PSpr9d9RYVbWpzOM= + +jest-changed-files@^23.4.2: + version "23.4.2" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-23.4.2.tgz#1eed688370cd5eebafe4ae93d34bb3b64968fe83" + integrity sha512-EyNhTAUWEfwnK0Is/09LxoqNDOn7mU7S3EHskG52djOFS/z+IT0jT3h3Ql61+dklcG7bJJitIWEMB4Sp1piHmA== + dependencies: + throat "^4.0.0" + +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" + integrity sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg== + dependencies: + "@jest/types" "^24.9.0" + execa "^1.0.0" + throat "^4.0.0" + +jest-cli@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-23.6.0.tgz#61ab917744338f443ef2baa282ddffdd658a5da4" + integrity sha512-hgeD1zRUp1E1zsiyOXjEn4LzRLWdJBV//ukAHGlx6s5mfCNJTbhbHjgxnDUXA8fsKWN/HqFFF6X5XcCwC/IvYQ== + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.1" + exit "^0.1.2" + glob "^7.1.2" + graceful-fs "^4.1.11" + import-local "^1.0.0" + is-ci "^1.0.10" + istanbul-api "^1.3.1" + istanbul-lib-coverage "^1.2.0" + istanbul-lib-instrument "^1.10.1" + istanbul-lib-source-maps "^1.2.4" + jest-changed-files "^23.4.2" + jest-config "^23.6.0" + jest-environment-jsdom "^23.4.0" + jest-get-type "^22.1.0" + jest-haste-map "^23.6.0" + jest-message-util "^23.4.0" + jest-regex-util "^23.3.0" + jest-resolve-dependencies "^23.6.0" + jest-runner "^23.6.0" + jest-runtime "^23.6.0" + jest-snapshot "^23.6.0" + jest-util "^23.4.0" + jest-validate "^23.6.0" + jest-watcher "^23.4.0" + jest-worker "^23.2.0" + micromatch "^2.3.11" + node-notifier "^5.2.1" + prompts "^0.1.9" + realpath-native "^1.0.0" + rimraf "^2.5.4" + slash "^1.0.0" + string-length "^2.0.0" + strip-ansi "^4.0.0" + which "^1.2.12" + yargs "^11.0.0" + +jest-cli@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.9.0.tgz#ad2de62d07472d419c6abc301fc432b98b10d2af" + integrity sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg== + dependencies: + "@jest/core" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + exit "^0.1.2" + import-local "^2.0.0" + is-ci "^2.0.0" + jest-config "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + prompts "^2.0.1" + realpath-native "^1.1.0" + yargs "^13.3.0" + +jest-config@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-23.6.0.tgz#f82546a90ade2d8c7026fbf6ac5207fc22f8eb1d" + integrity sha512-i8V7z9BeDXab1+VNo78WM0AtWpBRXJLnkT+lyT+Slx/cbP5sZJ0+NDuLcmBE5hXAoK0aUp7vI+MOxR+R4d8SRQ== + dependencies: + babel-core "^6.0.0" + babel-jest "^23.6.0" + chalk "^2.0.1" + glob "^7.1.1" + jest-environment-jsdom "^23.4.0" + jest-environment-node "^23.4.0" + jest-get-type "^22.1.0" + jest-jasmine2 "^23.6.0" + jest-regex-util "^23.3.0" + jest-resolve "^23.6.0" + jest-util "^23.4.0" + jest-validate "^23.6.0" + micromatch "^2.3.11" + pretty-format "^23.6.0" + +jest-config@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.9.0.tgz#fb1bbc60c73a46af03590719efa4825e6e4dd1b5" + integrity sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ== + dependencies: + "@babel/core" "^7.1.0" + "@jest/test-sequencer" "^24.9.0" + "@jest/types" "^24.9.0" + babel-jest "^24.9.0" + chalk "^2.0.1" + glob "^7.1.1" + jest-environment-jsdom "^24.9.0" + jest-environment-node "^24.9.0" + jest-get-type "^24.9.0" + jest-jasmine2 "^24.9.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + micromatch "^3.1.10" + pretty-format "^24.9.0" + realpath-native "^1.1.0" + +jest-diff@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-23.6.0.tgz#1500f3f16e850bb3d71233408089be099f610c7d" + integrity sha512-Gz9l5Ov+X3aL5L37IT+8hoCUsof1CVYBb2QEkOupK64XyRR3h+uRpYIm97K7sY8diFxowR8pIGEdyfMKTixo3g== + dependencies: + chalk "^2.0.1" + diff "^3.2.0" + jest-get-type "^22.1.0" + pretty-format "^23.6.0" + +jest-diff@^24.3.0, jest-diff@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" + integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== + dependencies: + chalk "^2.0.1" + diff-sequences "^24.9.0" + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + +jest-docblock@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-23.2.0.tgz#f085e1f18548d99fdd69b20207e6fd55d91383a7" + integrity sha1-8IXh8YVI2Z/dabICB+b9VdkTg6c= + dependencies: + detect-newline "^2.1.0" + +jest-docblock@^24.3.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.9.0.tgz#7970201802ba560e1c4092cc25cbedf5af5a8ce2" + integrity sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA== + dependencies: + detect-newline "^2.1.0" + +jest-each@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-23.6.0.tgz#ba0c3a82a8054387016139c733a05242d3d71575" + integrity sha512-x7V6M/WGJo6/kLoissORuvLIeAoyo2YqLOoCDkohgJ4XOXSqOtyvr8FbInlAWS77ojBsZrafbozWoKVRdtxFCg== + dependencies: + chalk "^2.0.1" + pretty-format "^23.6.0" + +jest-each@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.9.0.tgz#eb2da602e2a610898dbc5f1f6df3ba86b55f8b05" + integrity sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog== + dependencies: + "@jest/types" "^24.9.0" + chalk "^2.0.1" + jest-get-type "^24.9.0" + jest-util "^24.9.0" + pretty-format "^24.9.0" + +jest-environment-jsdom@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-23.4.0.tgz#056a7952b3fea513ac62a140a2c368c79d9e6023" + integrity sha1-BWp5UrP+pROsYqFAosNox52eYCM= + dependencies: + jest-mock "^23.2.0" + jest-util "^23.4.0" + jsdom "^11.5.1" + +jest-environment-jsdom@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz#4b0806c7fc94f95edb369a69cc2778eec2b7375b" + integrity sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA== + dependencies: + "@jest/environment" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + jest-util "^24.9.0" + jsdom "^11.5.1" + +jest-environment-node@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-23.4.0.tgz#57e80ed0841dea303167cce8cd79521debafde10" + integrity sha1-V+gO0IQd6jAxZ8zozXlSHeuv3hA= + dependencies: + jest-mock "^23.2.0" + jest-util "^23.4.0" + +jest-environment-node@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.9.0.tgz#333d2d2796f9687f2aeebf0742b519f33c1cbfd3" + integrity sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA== + dependencies: + "@jest/environment" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + jest-util "^24.9.0" + +jest-get-type@^22.1.0: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4" + integrity sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w== + +jest-get-type@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" + integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q== + +jest-haste-map@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-23.6.0.tgz#2e3eb997814ca696d62afdb3f2529f5bbc935e16" + integrity sha512-uyNhMyl6dr6HaXGHp8VF7cK6KpC6G9z9LiMNsst+rJIZ8l7wY0tk8qwjPmEghczojZ2/ZhtEdIabZ0OQRJSGGg== + dependencies: + fb-watchman "^2.0.0" + graceful-fs "^4.1.11" + invariant "^2.2.4" + jest-docblock "^23.2.0" + jest-serializer "^23.0.1" + jest-worker "^23.2.0" + micromatch "^2.3.11" + sane "^2.0.0" + +jest-haste-map@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" + integrity sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ== + dependencies: + "@jest/types" "^24.9.0" + anymatch "^2.0.0" + fb-watchman "^2.0.0" + graceful-fs "^4.1.15" + invariant "^2.2.4" + jest-serializer "^24.9.0" + jest-util "^24.9.0" + jest-worker "^24.9.0" + micromatch "^3.1.10" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^1.2.7" + +jest-jasmine2@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-23.6.0.tgz#840e937f848a6c8638df24360ab869cc718592e0" + integrity sha512-pe2Ytgs1nyCs8IvsEJRiRTPC0eVYd8L/dXJGU08GFuBwZ4sYH/lmFDdOL3ZmvJR8QKqV9MFuwlsAi/EWkFUbsQ== + dependencies: + babel-traverse "^6.0.0" + chalk "^2.0.1" + co "^4.6.0" + expect "^23.6.0" + is-generator-fn "^1.0.0" + jest-diff "^23.6.0" + jest-each "^23.6.0" + jest-matcher-utils "^23.6.0" + jest-message-util "^23.4.0" + jest-snapshot "^23.6.0" + jest-util "^23.4.0" + pretty-format "^23.6.0" + +jest-jasmine2@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz#1f7b1bd3242c1774e62acabb3646d96afc3be6a0" + integrity sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw== + dependencies: + "@babel/traverse" "^7.1.0" + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + co "^4.6.0" + expect "^24.9.0" + is-generator-fn "^2.0.0" + jest-each "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-runtime "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + pretty-format "^24.9.0" + throat "^4.0.0" + +jest-leak-detector@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-23.6.0.tgz#e4230fd42cf381a1a1971237ad56897de7e171de" + integrity sha512-f/8zA04rsl1Nzj10HIyEsXvYlMpMPcy0QkQilVZDFOaPbv2ur71X5u2+C4ZQJGyV/xvVXtCCZ3wQ99IgQxftCg== + dependencies: + pretty-format "^23.6.0" + +jest-leak-detector@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz#b665dea7c77100c5c4f7dfcb153b65cf07dcf96a" + integrity sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA== + dependencies: + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + +jest-matcher-utils@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-23.6.0.tgz#726bcea0c5294261a7417afb6da3186b4b8cac80" + integrity sha512-rosyCHQfBcol4NsckTn01cdelzWLU9Cq7aaigDf8VwwpIRvWE/9zLgX2bON+FkEW69/0UuYslUe22SOdEf2nog== + dependencies: + chalk "^2.0.1" + jest-get-type "^22.1.0" + pretty-format "^23.6.0" + +jest-matcher-utils@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" + integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA== + dependencies: + chalk "^2.0.1" + jest-diff "^24.9.0" + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + +jest-message-util@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-23.4.0.tgz#17610c50942349508d01a3d1e0bda2c079086a9f" + integrity sha1-F2EMUJQjSVCNAaPR4L2iwHkIap8= + dependencies: + "@babel/code-frame" "^7.0.0-beta.35" + chalk "^2.0.1" + micromatch "^2.3.11" + slash "^1.0.0" + stack-utils "^1.0.1" + +jest-message-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" + integrity sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/stack-utils" "^1.0.1" + chalk "^2.0.1" + micromatch "^3.1.10" + slash "^2.0.0" + stack-utils "^1.0.1" + +jest-mock@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-23.2.0.tgz#ad1c60f29e8719d47c26e1138098b6d18b261134" + integrity sha1-rRxg8p6HGdR8JuETgJi20YsmETQ= + +jest-mock@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.9.0.tgz#c22835541ee379b908673ad51087a2185c13f1c6" + integrity sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w== + dependencies: + "@jest/types" "^24.9.0" + +jest-pnp-resolver@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a" + integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== + +jest-regex-util@^23.3.0: + version "23.3.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-23.3.0.tgz#5f86729547c2785c4002ceaa8f849fe8ca471bc5" + integrity sha1-X4ZylUfCeFxAAs6qj4Sf6MpHG8U= + +jest-regex-util@^24.3.0, jest-regex-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" + integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== + +jest-resolve-dependencies@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-23.6.0.tgz#b4526af24c8540d9a3fab102c15081cf509b723d" + integrity sha512-EkQWkFWjGKwRtRyIwRwI6rtPAEyPWlUC2MpzHissYnzJeHcyCn1Hc8j7Nn1xUVrS5C6W5+ZL37XTem4D4pLZdA== + dependencies: + jest-regex-util "^23.3.0" + jest-snapshot "^23.6.0" + +jest-resolve-dependencies@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz#ad055198959c4cfba8a4f066c673a3f0786507ab" + integrity sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g== + dependencies: + "@jest/types" "^24.9.0" + jest-regex-util "^24.3.0" + jest-snapshot "^24.9.0" + +jest-resolve@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-23.6.0.tgz#cf1d1a24ce7ee7b23d661c33ba2150f3aebfa0ae" + integrity sha512-XyoRxNtO7YGpQDmtQCmZjum1MljDqUCob7XlZ6jy9gsMugHdN2hY4+Acz9Qvjz2mSsOnPSH7skBmDYCHXVZqkA== + dependencies: + browser-resolve "^1.11.3" + chalk "^2.0.1" + realpath-native "^1.0.0" + +jest-resolve@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.9.0.tgz#dff04c7687af34c4dd7e524892d9cf77e5d17321" + integrity sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ== + dependencies: + "@jest/types" "^24.9.0" + browser-resolve "^1.11.3" + chalk "^2.0.1" + jest-pnp-resolver "^1.2.1" + realpath-native "^1.1.0" + +jest-runner@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-23.6.0.tgz#3894bd219ffc3f3cb94dc48a4170a2e6f23a5a38" + integrity sha512-kw0+uj710dzSJKU6ygri851CObtCD9cN8aNkg8jWJf4ewFyEa6kwmiH/r/M1Ec5IL/6VFa0wnAk6w+gzUtjJzA== + dependencies: + exit "^0.1.2" + graceful-fs "^4.1.11" + jest-config "^23.6.0" + jest-docblock "^23.2.0" + jest-haste-map "^23.6.0" + jest-jasmine2 "^23.6.0" + jest-leak-detector "^23.6.0" + jest-message-util "^23.4.0" + jest-runtime "^23.6.0" + jest-util "^23.4.0" + jest-worker "^23.2.0" + source-map-support "^0.5.6" + throat "^4.0.0" + +jest-runner@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.9.0.tgz#574fafdbd54455c2b34b4bdf4365a23857fcdf42" + integrity sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg== + dependencies: + "@jest/console" "^24.7.1" + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.4.2" + exit "^0.1.2" + graceful-fs "^4.1.15" + jest-config "^24.9.0" + jest-docblock "^24.3.0" + jest-haste-map "^24.9.0" + jest-jasmine2 "^24.9.0" + jest-leak-detector "^24.9.0" + jest-message-util "^24.9.0" + jest-resolve "^24.9.0" + jest-runtime "^24.9.0" + jest-util "^24.9.0" + jest-worker "^24.6.0" + source-map-support "^0.5.6" + throat "^4.0.0" + +jest-runtime@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-23.6.0.tgz#059e58c8ab445917cd0e0d84ac2ba68de8f23082" + integrity sha512-ycnLTNPT2Gv+TRhnAYAQ0B3SryEXhhRj1kA6hBPSeZaNQkJ7GbZsxOLUkwg6YmvWGdX3BB3PYKFLDQCAE1zNOw== + dependencies: + babel-core "^6.0.0" + babel-plugin-istanbul "^4.1.6" + chalk "^2.0.1" + convert-source-map "^1.4.0" + exit "^0.1.2" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.1.11" + jest-config "^23.6.0" + jest-haste-map "^23.6.0" + jest-message-util "^23.4.0" + jest-regex-util "^23.3.0" + jest-resolve "^23.6.0" + jest-snapshot "^23.6.0" + jest-util "^23.4.0" + jest-validate "^23.6.0" + micromatch "^2.3.11" + realpath-native "^1.0.0" + slash "^1.0.0" + strip-bom "3.0.0" + write-file-atomic "^2.1.0" + yargs "^11.0.0" + +jest-runtime@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.9.0.tgz#9f14583af6a4f7314a6a9d9f0226e1a781c8e4ac" + integrity sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw== + dependencies: + "@jest/console" "^24.7.1" + "@jest/environment" "^24.9.0" + "@jest/source-map" "^24.3.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/yargs" "^13.0.0" + chalk "^2.0.1" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.1.15" + jest-config "^24.9.0" + jest-haste-map "^24.9.0" + jest-message-util "^24.9.0" + jest-mock "^24.9.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + realpath-native "^1.1.0" + slash "^2.0.0" + strip-bom "^3.0.0" + yargs "^13.3.0" + +jest-serializer-vue@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/jest-serializer-vue/-/jest-serializer-vue-2.0.2.tgz#b238ef286357ec6b480421bd47145050987d59b3" + integrity sha1-sjjvKGNX7GtIBCG9RxRQUJh9WbM= + dependencies: + pretty "2.0.0" + +jest-serializer@^23.0.1: + version "23.0.1" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-23.0.1.tgz#a3776aeb311e90fe83fab9e533e85102bd164165" + integrity sha1-o3dq6zEekP6D+rnlM+hRAr0WQWU= + +jest-serializer@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73" + integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ== + +jest-snapshot@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-23.6.0.tgz#f9c2625d1b18acda01ec2d2b826c0ce58a5aa17a" + integrity sha512-tM7/Bprftun6Cvj2Awh/ikS7zV3pVwjRYU2qNYS51VZHgaAMBs5l4o/69AiDHhQrj5+LA2Lq4VIvK7zYk/bswg== + dependencies: + babel-types "^6.0.0" + chalk "^2.0.1" + jest-diff "^23.6.0" + jest-matcher-utils "^23.6.0" + jest-message-util "^23.4.0" + jest-resolve "^23.6.0" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + pretty-format "^23.6.0" + semver "^5.5.0" + +jest-snapshot@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.9.0.tgz#ec8e9ca4f2ec0c5c87ae8f925cf97497b0e951ba" + integrity sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew== + dependencies: + "@babel/types" "^7.0.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + expect "^24.9.0" + jest-diff "^24.9.0" + jest-get-type "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-resolve "^24.9.0" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + pretty-format "^24.9.0" + semver "^6.2.0" + +jest-transform-stub@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/jest-transform-stub/-/jest-transform-stub-2.0.0.tgz#19018b0851f7568972147a5d60074b55f0225a7d" + integrity sha512-lspHaCRx/mBbnm3h4uMMS3R5aZzMwyNpNIJLXj4cEsV0mIUtS4IjYJLSoyjRCtnxb6RIGJ4NL2quZzfIeNhbkg== + +jest-util@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-23.4.0.tgz#4d063cb927baf0a23831ff61bec2cbbf49793561" + integrity sha1-TQY8uSe68KI4Mf9hvsLLv0l5NWE= + dependencies: + callsites "^2.0.0" + chalk "^2.0.1" + graceful-fs "^4.1.11" + is-ci "^1.0.10" + jest-message-util "^23.4.0" + mkdirp "^0.5.1" + slash "^1.0.0" + source-map "^0.6.0" + +jest-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.9.0.tgz#7396814e48536d2e85a37de3e4c431d7cb140162" + integrity sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg== + dependencies: + "@jest/console" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/source-map" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + callsites "^3.0.0" + chalk "^2.0.1" + graceful-fs "^4.1.15" + is-ci "^2.0.0" + mkdirp "^0.5.1" + slash "^2.0.0" + source-map "^0.6.0" + +jest-validate@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-23.6.0.tgz#36761f99d1ed33fcd425b4e4c5595d62b6597474" + integrity sha512-OFKapYxe72yz7agrDAWi8v2WL8GIfVqcbKRCLbRG9PAxtzF9b1SEDdTpytNDN12z2fJynoBwpMpvj2R39plI2A== + dependencies: + chalk "^2.0.1" + jest-get-type "^22.1.0" + leven "^2.1.0" + pretty-format "^23.6.0" + +jest-validate@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.9.0.tgz#0775c55360d173cd854e40180756d4ff52def8ab" + integrity sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ== + dependencies: + "@jest/types" "^24.9.0" + camelcase "^5.3.1" + chalk "^2.0.1" + jest-get-type "^24.9.0" + leven "^3.1.0" + pretty-format "^24.9.0" + +jest-watch-typeahead@0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/jest-watch-typeahead/-/jest-watch-typeahead-0.2.1.tgz#6c40f232996ca6c39977e929e9f79b189e7d87e4" + integrity sha512-xdhEtKSj0gmnkDQbPTIHvcMmXNUDzYpHLEJ5TFqlaI+schi2NI96xhWiZk9QoesAS7oBmKwWWsHazTrYl2ORgg== + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.4.1" + jest-watcher "^23.1.0" + slash "^2.0.0" + string-length "^2.0.0" + strip-ansi "^5.0.0" + +jest-watcher@^23.1.0, jest-watcher@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-23.4.0.tgz#d2e28ce74f8dad6c6afc922b92cabef6ed05c91c" + integrity sha1-0uKM50+NrWxq/JIrksq+9u0FyRw= + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.1" + string-length "^2.0.0" + +jest-watcher@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.9.0.tgz#4b56e5d1ceff005f5b88e528dc9afc8dd4ed2b3b" + integrity sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw== + dependencies: + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/yargs" "^13.0.0" + ansi-escapes "^3.0.0" + chalk "^2.0.1" + jest-util "^24.9.0" + string-length "^2.0.0" + +jest-worker@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-23.2.0.tgz#faf706a8da36fae60eb26957257fa7b5d8ea02b9" + integrity sha1-+vcGqNo2+uYOsmlXJX+ntdjqArk= + dependencies: + merge-stream "^1.0.1" + +jest-worker@^24.6.0, jest-worker@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" + integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== + dependencies: + merge-stream "^2.0.0" + supports-color "^6.1.0" + +jest@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-23.6.0.tgz#ad5835e923ebf6e19e7a1d7529a432edfee7813d" + integrity sha512-lWzcd+HSiqeuxyhG+EnZds6iO3Y3ZEnMrfZq/OTGvF/C+Z4fPMCdhWTGSAiO2Oym9rbEXfwddHhh6jqrTF3+Lw== + dependencies: + import-local "^1.0.0" + jest-cli "^23.6.0" + +jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-24.9.0.tgz#987d290c05a08b52c56188c1002e368edb007171" + integrity sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw== + dependencies: + import-local "^2.0.0" + jest-cli "^24.9.0" + +joi@^11.1.1: + version "11.4.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-11.4.0.tgz#f674897537b625e9ac3d0b7e1604c828ad913ccb" + integrity sha512-O7Uw+w/zEWgbL6OcHbyACKSj0PkQeUgmehdoXVSxt92QFCq4+1390Rwh5moI2K/OgC7D8RHRZqHZxT2husMJHA== + dependencies: + hoek "4.x.x" + isemail "3.x.x" + topo "2.x.x" + +js-beautify@^1.6.12, js-beautify@^1.6.14: + version "1.10.2" + resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.10.2.tgz#88c9099cd6559402b124cfab18754936f8a7b178" + integrity sha512-ZtBYyNUYJIsBWERnQP0rPN9KjkrDfJcMjuVGcvXOUJrD1zmOGwhRwQ4msG+HJ+Ni/FA7+sRQEMYVzdTQDvnzvQ== + dependencies: + config-chain "^1.1.12" + editorconfig "^0.15.3" + glob "^7.1.3" + mkdirp "~0.5.1" + nopt "~4.0.1" + +js-cookie@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" + integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== + +js-levenshtein@^1.1.3: + version "1.1.6" + resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" + integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== + +js-message@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/js-message/-/js-message-1.0.5.tgz#2300d24b1af08e89dd095bc1a4c9c9cfcb892d15" + integrity sha1-IwDSSxrwjondCVvBpMnJz8uJLRU= + +js-queue@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/js-queue/-/js-queue-2.0.0.tgz#362213cf860f468f0125fc6c96abc1742531f948" + integrity sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug= + dependencies: + easy-stack "^1.0.0" + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= + +js-yaml@^3.13.1, js-yaml@^3.7.0, js-yaml@^3.9.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsdom@^11.5.1: + version "11.12.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8" + integrity sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw== + dependencies: + abab "^2.0.0" + acorn "^5.5.3" + acorn-globals "^4.1.0" + array-equal "^1.0.0" + cssom ">= 0.3.2 < 0.4.0" + cssstyle "^1.0.0" + data-urls "^1.0.0" + domexception "^1.0.1" + escodegen "^1.9.1" + html-encoding-sniffer "^1.0.2" + left-pad "^1.3.0" + nwsapi "^2.0.7" + parse5 "4.0.0" + pn "^1.1.0" + request "^2.87.0" + request-promise-native "^1.0.5" + sax "^1.2.4" + symbol-tree "^3.2.2" + tough-cookie "^2.3.4" + w3c-hr-time "^1.0.1" + webidl-conversions "^4.0.2" + whatwg-encoding "^1.0.3" + whatwg-mimetype "^2.1.0" + whatwg-url "^6.4.1" + ws "^5.2.0" + xml-name-validator "^3.0.0" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s= + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + +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" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-schema-traverse@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json3@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" + integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== + +json5@2.x, json5@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6" + integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ== + dependencies: + minimist "^1.2.0" + +json5@^0.5.0, json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +killable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" + integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== + +kleur@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-2.0.2.tgz#b704f4944d95e255d038f0cb05fb8a602c55a300" + integrity sha512-77XF9iTllATmG9lSlIv0qdQ2BQ/h9t0bJllHlbvsQ0zUWfU7Yi0S8L5JXzPZgkefIiajLmBJJ4BsMJmqcf7oxQ== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +launch-editor-middleware@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/launch-editor-middleware/-/launch-editor-middleware-2.2.1.tgz#e14b07e6c7154b0a4b86a0fd345784e45804c157" + integrity sha512-s0UO2/gEGiCgei3/2UN3SMuUj1phjQN8lcpnvgLSz26fAzNWPQ6Nf/kF5IFClnfU2ehp6LrmKdMU/beveO+2jg== + dependencies: + launch-editor "^2.2.1" + +launch-editor@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.2.1.tgz#871b5a3ee39d6680fcc26d37930b6eeda89db0ca" + integrity sha512-On+V7K2uZK6wK7x691ycSUbLD/FyKKelArkbaAMSSJU8JmqmhwN2+mnJDNINuJWSrh2L0kDk+ZQtbC/gOWUwLw== + dependencies: + chalk "^2.3.0" + shell-quote "^1.6.1" + +lazy-ass@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= + +lcid@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" + integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== + dependencies: + invert-kv "^2.0.0" + +left-pad@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" + integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== + +leven@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" + integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA= + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + +listr-silent-renderer@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" + integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4= + +listr-update-renderer@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz#ca80e1779b4e70266807e8eed1ad6abe398550f9" + integrity sha1-yoDhd5tOcCZoB+ju0a1qvjmFUPk= + dependencies: + chalk "^1.1.3" + cli-truncate "^0.2.1" + elegant-spinner "^1.0.1" + figures "^1.7.0" + indent-string "^3.0.0" + log-symbols "^1.0.2" + log-update "^1.0.2" + strip-ansi "^3.0.1" + +listr-verbose-renderer@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#8206f4cf6d52ddc5827e5fd14989e0e965933a35" + integrity sha1-ggb0z21S3cWCfl/RSYng6WWTOjU= + dependencies: + chalk "^1.1.3" + cli-cursor "^1.0.2" + date-fns "^1.27.2" + figures "^1.7.0" + +listr@0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/listr/-/listr-0.12.0.tgz#6bce2c0f5603fa49580ea17cd6a00cc0e5fa451a" + integrity sha1-a84sD1YD+klYDqF81qAMwOX6RRo= + dependencies: + chalk "^1.1.3" + cli-truncate "^0.2.1" + figures "^1.7.0" + indent-string "^2.1.0" + is-promise "^2.1.0" + is-stream "^1.1.0" + listr-silent-renderer "^1.1.1" + listr-update-renderer "^0.2.0" + listr-verbose-renderer "^0.4.0" + log-symbols "^1.0.2" + log-update "^1.0.2" + ora "^0.2.3" + p-map "^1.1.1" + rxjs "^5.0.0-beta.11" + stream-to-observable "^0.1.0" + strip-ansi "^3.0.1" + +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" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + +loader-fs-cache@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/loader-fs-cache/-/loader-fs-cache-1.0.2.tgz#54cedf6b727e1779fd8f01205f05f6e88706f086" + integrity sha512-70IzT/0/L+M20jUlEqZhZyArTU6VKLRTYRDAYN26g4jfzpJqjipLL3/hgYpySqI9PwsVRHHFja0LfEmsx9X2Cw== + dependencies: + find-cache-dir "^0.1.1" + mkdirp "0.5.1" + +loader-runner@^2.3.1, loader-runner@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" + integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== + +loader-utils@^0.2.16: + version "0.2.17" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" + integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g= + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + object-assign "^4.0.1" + +loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" + integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== + dependencies: + big.js "^5.2.2" + emojis-list "^2.0.0" + json5 "^1.0.1" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + +lodash.defaultsdeep@^4.6.1: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz#512e9bd721d272d94e3d3a63653fa17516741ca6" + integrity sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA== + +lodash.kebabcase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" + integrity sha1-hImxyw0p/4gZXM7KRI/21swpXDY= + +lodash.mapvalues@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" + integrity sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw= + +lodash.memoize@4.x, lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + +lodash.once@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= + +lodash.template@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" + integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.templatesettings@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" + integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== + dependencies: + lodash._reinterpolate "^3.0.0" + +lodash.transform@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.transform/-/lodash.transform-4.6.0.tgz#12306422f63324aed8483d3f38332b5f670547a0" + integrity sha1-EjBkIvYzJK7YSD0/ODMrX2cFR6A= + +lodash.unescape@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" + integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= + +lodash@4.17.15, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.3.0: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + +log-symbols@2.2.0, log-symbols@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + +log-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" + integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg= + dependencies: + chalk "^1.0.0" + +log-update@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-1.0.2.tgz#19929f64c4093d2d2e7075a1dad8af59c296b8d1" + integrity sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE= + dependencies: + ansi-escapes "^1.0.0" + cli-cursor "^1.0.2" + +loglevel@^1.6.6: + version "1.6.6" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.6.tgz#0ee6300cc058db6b3551fa1c4bf73b83bb771312" + integrity sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ== + +loose-envify@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lower-case@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= + +lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2, lru-cache@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +make-dir@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== + dependencies: + pify "^3.0.0" + +make-dir@^2.0.0, make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +make-error@1.x: + version "1.3.5" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" + integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== + +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= + dependencies: + tmpl "1.0.x" + +mamacro@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" + integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA== + +map-age-cleaner@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +math-random@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" + integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +mdn-data@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" + integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +mem@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" + integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== + dependencies: + map-age-cleaner "^0.1.1" + mimic-fn "^2.0.0" + p-is-promise "^2.0.0" + +memory-fs@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +memory-fs@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" + integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merge-source-map@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" + integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== + dependencies: + source-map "^0.6.1" + +merge-stream@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" + integrity sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE= + dependencies: + readable-stream "^2.0.1" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.2.3: + version "1.3.0" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" + integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== + +merge@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" + integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +micromatch@^2.3.11: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.42.0, "mime-db@>= 1.40.0 < 2": + version "1.42.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.42.0.tgz#3e252907b4c7adb906597b4b65636272cf9e7bac" + integrity sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ== + +mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.25" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.25.tgz#39772d46621f93e2a80a856c53b86a62156a6437" + integrity sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg== + dependencies: + mime-db "1.42.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.0.3, mime@^2.4.4: + version "2.4.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" + integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + +mimic-fn@^2.0.0, mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mini-css-extract-plugin@^0.8.0: + version "0.8.2" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.2.tgz#a875e169beb27c88af77dd962771c9eedc3da161" + integrity sha512-a3Y4of27Wz+mqK3qrcd3VhYz6cU0iW5x3Sgvqzbj+XmlrSizmvu8QQMl5oMYJjgHOC4iyt+w7l4umP+dQeW3bw== + dependencies: + loader-utils "^1.1.0" + normalize-url "1.9.1" + schema-utils "^1.0.0" + webpack-sources "^1.1.0" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@1.2.0, minimist@^1.1.1, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= + +mississippi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f" + integrity sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw== + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^2.0.1" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mississippi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" + integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^3.0.0" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@0.5.1, mkdirp@0.x, mkdirp@^0.5.1, mkdirp@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +moment@2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" + integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== + +move-concurrently@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= + dependencies: + aproba "^1.1.1" + copy-concurrently "^1.0.0" + fs-write-stream-atomic "^1.0.8" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.3" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +multicast-dns-service-types@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" + integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= + +multicast-dns@^6.0.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" + integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== + dependencies: + dns-packet "^1.3.1" + thunky "^1.0.2" + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +mz@^2.4.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nan@^2.12.1: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" + integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +no-case@^2.2.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== + dependencies: + lower-case "^1.1.1" + +node-cache@^4.1.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-4.2.1.tgz#efd8474dee4edec4138cdded580f5516500f7334" + integrity sha512-BOb67bWg2dTyax5kdef5WfU3X8xu4wPg+zHzkvls0Q/QpYycIFRLEEIdAx9Wma43DxG6Qzn4illdZoYseKWa4A== + dependencies: + clone "2.x" + lodash "^4.17.15" + +node-forge@0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" + integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + +node-ipc@^9.1.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/node-ipc/-/node-ipc-9.1.1.tgz#4e245ed6938e65100e595ebc5dc34b16e8dd5d69" + integrity sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w== + dependencies: + event-pubsub "4.3.0" + js-message "1.0.5" + js-queue "2.0.0" + +node-libs-browser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" + integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^3.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.1" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.11.0" + vm-browserify "^1.0.1" + +node-modules-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" + integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= + +node-notifier@^5.2.1, node-notifier@^5.4.2: + version "5.4.3" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.3.tgz#cb72daf94c93904098e28b9c590fd866e464bd50" + integrity sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q== + dependencies: + growly "^1.3.0" + is-wsl "^1.1.0" + semver "^5.5.0" + shellwords "^0.1.1" + which "^1.3.0" + +node-releases@^1.1.44: + version "1.1.44" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.44.tgz#cd66438a6eb875e3eb012b6a12e48d9f4326ffd7" + integrity sha512-NwbdvJyR7nrcGrXvKAvzc5raj/NkoJudkarh2yIpJ4t0NH4aqjUDz/486P+ynIW5eokKOfzGNRdYoLfBlomruw== + dependencies: + semver "^6.3.0" + +nopt@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379" + integrity sha1-MtDkcvkf80VwHBWoMRAY07CpA3k= + +normalize-path@^2.0.1, normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= + +normalize-url@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" + integrity sha1-LMDWazHqIwNkWENuNiDYWVTGbDw= + dependencies: + object-assign "^4.0.1" + prepend-http "^1.0.0" + query-string "^4.1.0" + sort-keys "^1.0.0" + +normalize-url@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" + integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== + +normalize-wheel@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/normalize-wheel/-/normalize-wheel-1.0.1.tgz#aec886affdb045070d856447df62ecf86146ec45" + integrity sha1-rsiGr/2wRQcNhWRH32Ls+GFG7EU= + +normalize.css@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3" + integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg== + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +nprogress@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" + integrity sha1-y480xTIT2JVyP8urkH6UIq28r7E= + +nth-check@^1.0.2, nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +nwsapi@^2.0.7: + version "2.2.0" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-hash@^1.1.4: + version "1.3.1" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" + integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== + +object-inspect@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" + integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== + +object-is@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4" + integrity sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ== + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.getownpropertydescriptors@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" + integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo= + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +object.values@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" + integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + +onetime@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" + integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q== + dependencies: + mimic-fn "^2.1.0" + +open@^6.3.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" + integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== + dependencies: + is-wsl "^1.1.0" + +opener@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed" + integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA== + +opn@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== + dependencies: + is-wsl "^1.1.0" + +optimist@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= + dependencies: + minimist "~0.0.1" + wordwrap "~0.0.2" + +optionator@^0.8.1, optionator@^0.8.2, optionator@^0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +ora@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4" + integrity sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q= + dependencies: + chalk "^1.1.1" + cli-cursor "^1.0.2" + cli-spinners "^0.1.2" + object-assign "^4.0.1" + +ora@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" + integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== + dependencies: + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-spinners "^2.0.0" + log-symbols "^2.2.0" + strip-ansi "^5.2.0" + wcwidth "^1.0.1" + +original@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" + integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== + dependencies: + url-parse "^1.4.3" + +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= + +os-homedir@^1.0.0, os-homedir@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-locale@^3.0.0, os-locale@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" + integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== + dependencies: + execa "^1.0.0" + lcid "^2.0.0" + mem "^4.0.0" + +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= + +p-each-series@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71" + integrity sha1-kw89Et0fUOdDRFeiLNbwSsatf3E= + dependencies: + p-reduce "^1.0.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-finally@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" + integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== + +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" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== + +p-limit@^1.0.0, p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" + integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-map@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" + integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA== + +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + +p-reduce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" + integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= + +p-retry@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" + integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w== + dependencies: + retry "^0.12.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pako@~1.0.5: + version "1.0.10" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" + integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== + +parallel-transform@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" + integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== + dependencies: + cyclist "^1.0.1" + inherits "^2.0.3" + readable-stream "^2.1.5" + +param-case@2.1.x: + version "2.1.1" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" + integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= + dependencies: + no-case "^2.2.0" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0: + version "5.1.5" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e" + integrity sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ== + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse-json@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.0.0.tgz#73e5114c986d143efa3712d4ea24db9a4266f60f" + integrity sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + lines-and-columns "^1.1.6" + +parse5-htmlparser2-tree-adapter@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-5.1.1.tgz#e8c743d4e92194d5293ecde2b08be31e67461cbc" + integrity sha512-CF+TKjXqoqyDwHqBhFQ+3l5t83xYi6fVT1tQNg+Ye0JRLnTxWvIroCjEp1A0k4lneHNBGnICUf0cfYVYGEazqw== + dependencies: + parse5 "^5.1.1" + +parse5@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" + integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== + +parse5@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" + integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" + integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@^1.0.1, path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.5, path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +path-to-regexp@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-3.2.0.tgz#fa7877ecbc495c601907562222453c43cc204a5f" + integrity sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA== + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= + dependencies: + pify "^2.0.0" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + +pbkdf2@^3.0.3: + version "3.0.17" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" + integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picomatch@^2.0.4, picomatch@^2.0.7: + version "2.1.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.1.1.tgz#ecdfbea7704adb5fe6fb47f9866c4c0e15e905c5" + integrity sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA== + +pify@^2.0.0, pify@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +pirates@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" + integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== + dependencies: + node-modules-regexp "^1.0.0" + +pkg-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" + integrity sha1-ektQio1bstYp1EcFb/TpyTFM89Q= + dependencies: + find-up "^1.0.0" + +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + +pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" + integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= + dependencies: + find-up "^2.1.0" + +pluralize@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" + integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow== + +pn@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" + integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== + +portfinder@^1.0.20, portfinder@^1.0.25: + version "1.0.25" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca" + integrity sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg== + dependencies: + async "^2.6.2" + debug "^3.1.1" + mkdirp "^0.5.1" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +postcss-calc@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.1.tgz#36d77bab023b0ecbb9789d84dcb23c4941145436" + integrity sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ== + dependencies: + css-unit-converter "^1.1.1" + postcss "^7.0.5" + postcss-selector-parser "^5.0.0-rc.4" + postcss-value-parser "^3.3.1" + +postcss-colormin@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" + integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== + dependencies: + browserslist "^4.0.0" + color "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-convert-values@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" + integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-discard-comments@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" + integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== + dependencies: + postcss "^7.0.0" + +postcss-discard-duplicates@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" + integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== + dependencies: + postcss "^7.0.0" + +postcss-discard-empty@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" + integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== + dependencies: + postcss "^7.0.0" + +postcss-discard-overridden@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" + integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== + dependencies: + postcss "^7.0.0" + +postcss-load-config@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.0.tgz#c84d692b7bb7b41ddced94ee62e8ab31b417b003" + integrity sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q== + dependencies: + cosmiconfig "^5.0.0" + import-cwd "^2.0.0" + +postcss-loader@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d" + integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA== + dependencies: + loader-utils "^1.1.0" + postcss "^7.0.0" + postcss-load-config "^2.0.0" + schema-utils "^1.0.0" + +postcss-merge-longhand@^4.0.11: + version "4.0.11" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" + integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== + dependencies: + css-color-names "0.0.4" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + stylehacks "^4.0.0" + +postcss-merge-rules@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" + integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + cssnano-util-same-parent "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + vendors "^1.0.0" + +postcss-minify-font-values@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" + integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-gradients@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" + integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== + dependencies: + cssnano-util-get-arguments "^4.0.0" + is-color-stop "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-params@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" + integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== + dependencies: + alphanum-sort "^1.0.0" + browserslist "^4.0.0" + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + uniqs "^2.0.0" + +postcss-minify-selectors@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" + integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== + dependencies: + alphanum-sort "^1.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + +postcss-modules-extract-imports@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz#dc87e34148ec7eab5f791f7cd5849833375b741a" + integrity sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw== + dependencies: + postcss "^6.0.1" + +postcss-modules-local-by-default@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069" + integrity sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk= + dependencies: + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" + +postcss-modules-scope@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90" + integrity sha1-1upkmUx5+XtipytCb75gVqGUu5A= + dependencies: + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" + +postcss-modules-values@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20" + integrity sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA= + dependencies: + icss-replace-symbols "^1.1.0" + postcss "^6.0.1" + +postcss-normalize-charset@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" + integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== + dependencies: + postcss "^7.0.0" + +postcss-normalize-display-values@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" + integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-positions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" + integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== + dependencies: + cssnano-util-get-arguments "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-repeat-style@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" + integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== + dependencies: + cssnano-util-get-arguments "^4.0.0" + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-string@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" + integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== + dependencies: + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-timing-functions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" + integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-unicode@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" + integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-url@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" + integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== + dependencies: + is-absolute-url "^2.0.0" + normalize-url "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-whitespace@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" + integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-ordered-values@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" + integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== + dependencies: + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-reduce-initial@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" + integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + +postcss-reduce-transforms@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" + integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== + dependencies: + cssnano-util-get-match "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-selector-parser@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865" + integrity sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU= + dependencies: + dot-prop "^4.1.1" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^5.0.0, postcss-selector-parser@^5.0.0-rc.4: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz#249044356697b33b64f1a8f7c80922dddee7195c" + integrity sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ== + dependencies: + cssesc "^2.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-svgo@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258" + integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw== + dependencies: + is-svg "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + svgo "^1.0.0" + +postcss-unique-selectors@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" + integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== + dependencies: + alphanum-sort "^1.0.0" + postcss "^7.0.0" + uniqs "^2.0.0" + +postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" + integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== + +postcss-value-parser@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz#482282c09a42706d1fc9a069b73f44ec08391dc9" + integrity sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ== + +postcss@^6.0.1, postcss@^6.0.23: + version "6.0.23" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" + integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag== + dependencies: + chalk "^2.4.1" + source-map "^0.6.1" + supports-color "^5.4.0" + +postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.23, postcss@^7.0.5: + version "7.0.26" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.26.tgz#5ed615cfcab35ba9bbb82414a4fa88ea10429587" + integrity sha512-IY4oRjpXWYshuTDFxMVkJDtWIk2LhsTlu8bZnbEJA4+bYT16Lvpo8Qv6EvDumhYRgzjZl489pmsY3qVgJQ08nA== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +prepend-http@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= + +prettier@^1.18.2: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + +pretty-bytes@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9" + integrity sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk= + +pretty-error@^2.0.2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" + integrity sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM= + dependencies: + renderkid "^2.0.1" + utila "~0.4" + +pretty-format@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760" + integrity sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw== + dependencies: + ansi-regex "^3.0.0" + ansi-styles "^3.2.0" + +pretty-format@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" + integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== + dependencies: + "@jest/types" "^24.9.0" + ansi-regex "^4.0.0" + ansi-styles "^3.2.0" + react-is "^16.8.4" + +pretty@2.0.0, pretty@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pretty/-/pretty-2.0.0.tgz#adbc7960b7bbfe289a557dc5f737619a220d06a5" + integrity sha1-rbx5YLe7/iiaVX3F9zdhmiINBqU= + dependencies: + condense-newlines "^0.2.1" + extend-shallow "^2.0.1" + js-beautify "^1.6.12" + +private@^0.1.6, private@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= + +prompts@^0.1.9: + version "0.1.14" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-0.1.14.tgz#a8e15c612c5c9ec8f8111847df3337c9cbd443b2" + integrity sha512-rxkyiE9YH6zAz/rZpywySLKkpaj0NMVyNw1qhsubdbjjSgcayjTShDreZGlFMcGSu5sab3bAKPfFk78PB90+8w== + dependencies: + kleur "^2.0.1" + sisteransi "^0.1.1" + +prompts@^2.0.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.0.tgz#a444e968fa4cc7e86689a74050685ac8006c4cc4" + integrity sha512-NfbbPPg/74fT7wk2XYQ7hAIp9zJyZp5Fu19iRbORqqy1BhtrkZ0fPafBU+7bmn8ie69DpT0R6QpJIN2oisYjJg== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.3" + +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= + +proxy-addr@~2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" + integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.9.0" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +psl@^1.1.24, psl@^1.1.28: + version "1.7.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" + integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^2.0.0, pump@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@2.x.x, punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +punycode@^1.2.4, punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +q@^1.1.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +query-string@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" + integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s= + dependencies: + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +querystringify@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" + integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== + +ramda@0.24.1: + version "0.24.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857" + integrity sha1-w7d1UZfzW43DUCIoJixMkd22uFc= + +randomatic@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" + integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw== + dependencies: + is-number "^4.0.0" + kind-of "^6.0.0" + math-random "^1.0.1" + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +react-is@^16.8.4: + version "16.12.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" + integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q== + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg-up@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" + integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== + dependencies: + find-up "^3.0.0" + read-pkg "^3.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + +read-pkg@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6, readable-stream@^3.1.1: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" + integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +readdirp@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.3.0.tgz#984458d13a1e42e2e9f5841b129e162f369aff17" + integrity sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ== + dependencies: + picomatch "^2.0.7" + +realpath-native@^1.0.0, realpath-native@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" + integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== + dependencies: + util.promisify "^1.0.0" + +regenerate-unicode-properties@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" + integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA== + dependencies: + regenerate "^1.4.0" + +regenerate@^1.2.1, regenerate@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== + +regenerator-runtime@^0.13.2: + version "0.13.3" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" + integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== + +regenerator-transform@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb" + integrity sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ== + dependencies: + private "^0.1.6" + +regex-cache@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ== + dependencies: + is-equal-shallow "^0.1.3" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexp.prototype.flags@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" + integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +regexpp@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" + integrity sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw== + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +regexpu-core@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" + integrity sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs= + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regexpu-core@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6" + integrity sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.1.0" + regjsgen "^0.5.0" + regjsparser "^0.6.0" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.1.0" + +register-service-worker@^1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/register-service-worker/-/register-service-worker-1.6.2.tgz#9297e54c205c371c6e49bfa88f6997e8dd315f4c" + integrity sha512-I8L87fX2TK29LDx+wgyOUh2BJ3rDIRC1FtRZEHeP3rivzDv6p1DDZLGGtPucqjEkm45+2crtFIFssEWv56+9Wg== + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc= + +regjsgen@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c" + integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg== + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw= + dependencies: + jsesc "~0.5.0" + +regjsparser@^0.6.0: + version "0.6.2" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.2.tgz#fd62c753991467d9d1ffe0a9f67f27a529024b96" + integrity sha512-E9ghzUtoLwDekPT0DYCp+c4h+bvuUpe6rRHCTYn6eGoqj1LgKXxT6I0Il4WbjhQkOghzi/V+y03bPKvbllL93Q== + dependencies: + jsesc "~0.5.0" + +relateurl@0.2.x: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +renderkid@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.3.tgz#380179c2ff5ae1365c522bf2fcfcff01c5b74149" + integrity sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA== + dependencies: + css-select "^1.1.0" + dom-converter "^0.2" + htmlparser2 "^3.3.0" + strip-ansi "^3.0.0" + utila "^0.4.0" + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.5.2, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + +request-progress@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" + integrity sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4= + dependencies: + throttleit "^1.0.0" + +request-promise-core@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" + integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ== + dependencies: + lodash "^4.17.15" + +request-promise-native@^1.0.5, request-promise-native@^1.0.7: + version "1.0.8" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" + integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== + dependencies: + request-promise-core "1.1.3" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" + +request@2.88.0, request@^2.87.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.0" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +require-uncached@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" + integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM= + dependencies: + caller-path "^0.1.0" + resolve-from "^1.0.0" + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +reselect@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147" + integrity sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc= + +resize-observer-polyfill@^1.5.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= + dependencies: + resolve-from "^3.0.0" + +resolve-from@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" + integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY= + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= + +resolve@1.x, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.5.0, resolve@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.1.tgz#9e018c540fcf0c427d678b9931cbf45e984bcaff" + integrity sha512-fn5Wobh4cxbLzuHaE+nphztHy43/b++4M6SsGFC2gB8uYwf0C8LcarfCz1un7UTW8OFQg9iNjZ4xpcFVGebDPg== + dependencies: + path-parse "^1.0.6" + +restore-cursor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" + integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= + dependencies: + exit-hook "^1.0.0" + onetime "^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" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + +rgb-regex@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" + integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= + +rgba-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" + integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= + +rimraf@2.6.3, rimraf@~2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rsvp@^3.3.3: + version "3.6.2" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a" + integrity sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw== + +rsvp@^4.8.4: + version "4.8.5" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== + +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= + dependencies: + is-promise "^2.1.0" + +run-queue@^1.0.0, run-queue@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= + dependencies: + aproba "^1.1.1" + +rx-lite-aggregates@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" + integrity sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74= + dependencies: + rx-lite "*" + +rx-lite@*, rx-lite@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" + integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ= + +rxjs@^5.0.0-beta.11: + version "5.5.12" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.12.tgz#6fa61b8a77c3d793dbaf270bee2f43f652d741cc" + integrity sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw== + dependencies: + symbol-observable "1.0.1" + +rxjs@^6.5.3: + version "6.5.4" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c" + integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q== + dependencies: + tslib "^1.9.0" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sane@^2.0.0: + version "2.5.2" + resolved "https://registry.yarnpkg.com/sane/-/sane-2.5.2.tgz#b4dc1861c21b427e929507a3e751e2a2cb8ab3fa" + integrity sha1-tNwYYcIbQn6SlQej51HiosuKs/o= + dependencies: + anymatch "^2.0.0" + capture-exit "^1.2.0" + exec-sh "^0.2.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + watch "~0.18.0" + optionalDependencies: + fsevents "^1.2.3" + +sane@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== + dependencies: + "@cnakazawa/watch" "^1.0.3" + anymatch "^2.0.0" + capture-exit "^2.0.0" + exec-sh "^0.3.2" + execa "^1.0.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + +sass-loader@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-7.3.1.tgz#a5bf68a04bcea1c13ff842d747150f7ab7d0d23f" + integrity sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA== + dependencies: + clone-deep "^4.0.1" + loader-utils "^1.0.1" + neo-async "^2.5.0" + pify "^4.0.1" + semver "^6.3.0" + +sass@^1.22.10: + version "1.24.1" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.24.1.tgz#5b32ef0ee04403939d96a4963a42fd90e9408691" + integrity sha512-lMhl1V4Bj/lm+5c4sIt3fVzfmkyqqztpypr9dI8N0XsaeLs+gWow0der0e+I9WsRQK86SLXfEUgleERqPbR5Jw== + dependencies: + chokidar ">=2.0.0 <4.0.0" + +sax@^1.2.4, sax@~1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +schema-utils@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" + integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== + dependencies: + ajv "^6.1.0" + ajv-errors "^1.0.0" + ajv-keywords "^3.1.0" + +schema-utils@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.1.tgz#eb78f0b945c7bcfa2082b3565e8db3548011dc4f" + integrity sha512-0WXHDs1VDJyo+Zqs9TKLKyD/h7yDpHUhEFsM2CzkICFdoX1av+GBq/J2xRTFfsQO5kBfhZzANf2VcIm84jqDbg== + dependencies: + ajv "^6.10.2" + ajv-keywords "^3.4.1" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= + +selfsigned@^1.10.7: + version "1.10.7" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.7.tgz#da5819fd049d5574f28e88a9bcc6dbc6e6f3906b" + integrity sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA== + dependencies: + node-forge "0.9.0" + +"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.3.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" + integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== + +semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serialize-javascript@^1.4.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.9.1.tgz#cfc200aef77b600c47da9bb8149c943e798c2fdb" + integrity sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A== + +serialize-javascript@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" + integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@^1.6.1: + version "1.7.2" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" + integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== + +shellwords@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== + +sigmund@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= + dependencies: + is-arrayish "^0.3.1" + +sisteransi@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-0.1.1.tgz#5431447d5f7d1675aac667ccd0b865a4994cb3ce" + integrity sha512-PmGOd02bM9YO5ifxpw36nrNMBTptEtfRl4qUYl9SndkolplkrZZOW7PGHjrZL53QvMVj9nQ+TKqUnRsw4tJa4g== + +sisteransi@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.4.tgz#386713f1ef688c7c0304dc4c0632898941cad2e3" + integrity sha512-/ekMoM4NJ59ivGSfKapeG+FWtrmWvA1p6FBZwXrqojw90vJu8lBmrTxCMuBCydKtkaUe2zt4PlxeTKpjwMbyig== + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= + +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= + +slice-ansi@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" + integrity sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg== + dependencies: + is-fullwidth-code-point "^2.0.0" + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +sockjs-client@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" + integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== + dependencies: + debug "^3.2.5" + eventsource "^1.0.7" + faye-websocket "~0.11.1" + inherits "^2.0.3" + json3 "^3.3.2" + url-parse "^1.4.3" + +sockjs@0.3.19: + version "0.3.19" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d" + integrity sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw== + dependencies: + faye-websocket "^0.10.0" + uuid "^3.0.1" + +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= + dependencies: + is-plain-obj "^1.0.0" + +source-list-map@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.4.15: + version "0.4.18" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" + integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA== + dependencies: + source-map "^0.5.6" + +source-map-support@^0.5.6, source-map-support@~0.5.12: + version "0.5.16" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" + integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spdx-correct@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" + integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" + integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== + +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.5" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" + integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.1.tgz#6f12ed1c5db7ea4f24ebb8b89ba58c87c08257f2" + integrity sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +ssri@^5.2.4: + version "5.3.0" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.3.0.tgz#ba3872c9c6d33a0704a7d71ff045e5ec48999d06" + integrity sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ== + dependencies: + safe-buffer "^5.1.1" + +ssri@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" + integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== + dependencies: + figgy-pudding "^3.5.1" + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +stack-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" + integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== + +stackframe@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.1.0.tgz#e3fc2eb912259479c9822f7d1f1ff365bd5cbc83" + integrity sha512-Vx6W1Yvy+AM1R/ckVwcHQHV147pTPBKWCRLrXMuPrFVfvBUc3os7PR1QLIWCMhPpRg5eX9ojzbQIMLGBwyLjqg== + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +stealthy-require@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= + +stream-browserify@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" + integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-each@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" + integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== + dependencies: + end-of-stream "^1.1.0" + stream-shift "^1.0.0" + +stream-http@^2.7.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + +stream-to-observable@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.1.0.tgz#45bf1d9f2d7dc09bed81f1c307c430e68b84cffe" + integrity sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4= + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + +string-length@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" + integrity sha1-1A27aGo6zpYMHP/KVivyxF+DY+0= + dependencies: + astral-regex "^1.0.0" + strip-ansi "^4.0.0" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.padend@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.0.tgz#dc08f57a8010dc5c153550318f67e13adbb72ac3" + integrity sha512-3aIv8Ffdp8EZj8iLwREGpQaUZiPyrWrpzMBHvkiSW/bK/EGve9np07Vwy7IJ5waydpGXzQZu/F8Oze2/IWkBaA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +string.prototype.padstart@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.padstart/-/string.prototype.padstart-3.1.0.tgz#b47c087540d0710be5a49375751a0a627bd4ff90" + integrity sha512-envqZvUp2JItI+OeQ5UAh1ihbAV5G/2bixTojvlIa090GGqF+NQRxbWb2nv9fTGrZABv6+pE6jXoAZhhS2k4Hw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +string.prototype.trimleft@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" + integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +string.prototype.trimright@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" + integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +string_decoder@^1.0.0, string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +stringify-object@^3.2.2: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-bom@3.0.0, strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-comments@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-1.0.2.tgz#82b9c45e7f05873bee53f37168af930aa368679d" + integrity sha512-kL97alc47hoyIQSV165tTt9rG5dn4w1dNnBhOQ3bOU1Nc1hel09jnXANaHJ7vzHLd4Ju8kseDGzlev96pghLFw== + dependencies: + babel-extract-comments "^1.0.0" + babel-plugin-transform-object-rest-spread "^6.26.0" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-indent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" + integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= + +strip-json-comments@^2.0.0, strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +strip-json-comments@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" + integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== + +style-resources-loader@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/style-resources-loader/-/style-resources-loader-1.3.3.tgz#e4b3ab93e7c3d1606e86f9549522a0b5c4ad6812" + integrity sha512-vDD2HyG6On8H9gWUN9O9q1eXR/JnXpCkNvpusvgFsRQ9JZGF9drzvwKEigR9vqlmUbXO2t/vIIabpYMmis0eAQ== + dependencies: + glob "^7.1.6" + is-promise "^2.1.0" + loader-utils "^1.2.3" + schema-utils "^2.6.1" + +stylehacks@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" + integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + +supports-color@5.5.0, supports-color@^5.3.0, supports-color@^5.4.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +supports-color@^3.1.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= + dependencies: + has-flag "^1.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + +svg-tags@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" + integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q= + +svgo@^1.0.0, svgo@^1.0.5: + version "1.3.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" + integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== + dependencies: + chalk "^2.4.1" + coa "^2.0.2" + css-select "^2.0.0" + css-select-base-adapter "^0.1.1" + css-tree "1.0.0-alpha.37" + csso "^4.0.2" + js-yaml "^3.13.1" + mkdirp "~0.5.1" + object.values "^1.1.0" + sax "~1.2.4" + stable "^0.1.8" + unquote "~1.1.1" + util.promisify "~1.0.0" + +symbol-observable@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" + integrity sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ= + +symbol-tree@^3.2.2: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +table@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" + integrity sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA== + dependencies: + ajv "^5.2.3" + ajv-keywords "^2.1.0" + chalk "^2.1.0" + lodash "^4.17.4" + slice-ansi "1.0.0" + string-width "^2.1.1" + +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== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +tapable@^1.0.0, tapable@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + +terser-webpack-plugin@^1.2.3, terser-webpack-plugin@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz#5ecaf2dbdc5fb99745fd06791f46fc9ddb1c9a7c" + integrity sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA== + dependencies: + cacache "^12.0.2" + find-cache-dir "^2.1.0" + is-wsl "^1.1.0" + schema-utils "^1.0.0" + serialize-javascript "^2.1.2" + source-map "^0.6.1" + terser "^4.1.2" + webpack-sources "^1.4.0" + worker-farm "^1.7.0" + +terser@^4.1.2: + version "4.5.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.5.1.tgz#63b52d6b6ce344aa6fedcd0ee06a695799eb50bd" + integrity sha512-lH9zLIbX8PRBEFCTvfHGCy0s9HEKnNso1Dx9swSopF3VUnFLB8DpQ61tHxoofovNC/sG0spajJM3EIIRSTByiQ== + dependencies: + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" + +test-exclude@^4.2.1: + version "4.2.3" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.3.tgz#a9a5e64474e4398339245a0a769ad7c2f4a97c20" + integrity sha512-SYbXgY64PT+4GAL2ocI3HwPa4Q4TBKm0cwAVeKOt/Aoc0gSpNRjJX8w0pA1LMKZ3LBmd8pYBqApFNQLII9kavA== + dependencies: + arrify "^1.0.1" + micromatch "^2.3.11" + object-assign "^4.1.0" + read-pkg-up "^1.0.1" + require-main-filename "^1.0.1" + +test-exclude@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" + integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== + dependencies: + glob "^7.1.3" + minimatch "^3.0.4" + read-pkg-up "^4.0.0" + require-main-filename "^2.0.0" + +text-table@^0.2.0, text-table@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.0" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839" + integrity sha1-5p44obq+lpsBCCB5eLn2K4hgSDk= + dependencies: + any-promise "^1.0.0" + +thread-loader@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/thread-loader/-/thread-loader-2.1.3.tgz#cbd2c139fc2b2de6e9d28f62286ab770c1acbdda" + integrity sha512-wNrVKH2Lcf8ZrWxDF/khdlLlsTMczdcwPA9VEK4c2exlEPynYWxi9op3nPTo5lAnDIkE0rQEB3VBP+4Zncc9Hg== + dependencies: + loader-runner "^2.3.1" + loader-utils "^1.1.0" + neo-async "^2.6.0" + +throat@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" + integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo= + +throttle-debounce@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-1.1.0.tgz#51853da37be68a155cb6e827b3514a3c422e89cd" + integrity sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg== + +throttleit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" + integrity sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw= + +through2@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +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= + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +timers-browserify@^2.0.4: + version "2.0.11" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f" + integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ== + dependencies: + setimmediate "^1.0.4" + +timsort@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" + integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= + +tmp@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" + integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== + dependencies: + rimraf "^2.6.3" + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +topo@2.x.x: + version "2.0.2" + resolved "https://registry.yarnpkg.com/topo/-/topo-2.0.2.tgz#cd5615752539057c0dc0491a621c3bc6fbe1d182" + integrity sha1-zVYVdSU5BXwNwEkaYhw7xvvh0YI= + dependencies: + hoek "4.x.x" + +toposort@^1.0.0: + version "1.0.7" + resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029" + integrity sha1-LmhELZ9k7HILjMieZEOsbKqVACk= + +tough-cookie@^2.3.3, tough-cookie@^2.3.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tough-cookie@~2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== + dependencies: + psl "^1.1.24" + punycode "^1.4.1" + +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= + dependencies: + punycode "^2.1.0" + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= + +tryer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" + integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== + +ts-jest@^24.0.2: + version "24.2.0" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-24.2.0.tgz#7abca28c2b4b0a1fdd715cd667d65d047ea4e768" + integrity sha512-Yc+HLyldlIC9iIK8xEN7tV960Or56N49MDP7hubCZUeI7EbIOTsas6rXCMB4kQjLACJ7eDOF4xWEO5qumpKsag== + dependencies: + bs-logger "0.x" + buffer-from "1.x" + fast-json-stable-stringify "2.x" + json5 "2.x" + lodash.memoize "4.x" + make-error "1.x" + mkdirp "0.x" + resolve "1.x" + semver "^5.5" + yargs-parser "10.x" + +ts-loader@^5.3.3: + version "5.4.5" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-5.4.5.tgz#a0c1f034b017a9344cef0961bfd97cc192492b8b" + integrity sha512-XYsjfnRQCBum9AMRZpk2rTYSVpdZBpZK+kDh0TeT3kxmQNBDVIeUjdPjY5RZry4eIAb8XHc4gYSUiUWPYvzSRw== + dependencies: + chalk "^2.3.0" + enhanced-resolve "^4.0.0" + loader-utils "^1.0.2" + micromatch "^3.1.4" + semver "^5.0.1" + +tsconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" + integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== + dependencies: + "@types/strip-bom" "^3.0.0" + "@types/strip-json-comments" "0.0.30" + strip-bom "^3.0.0" + strip-json-comments "^2.0.0" + +tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== + +tslint@^5.15.0: + version "5.20.1" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz#e401e8aeda0152bc44dd07e614034f3f80c67b7d" + integrity sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg== + dependencies: + "@babel/code-frame" "^7.0.0" + builtin-modules "^1.1.1" + chalk "^2.3.0" + commander "^2.12.1" + diff "^4.0.1" + glob "^7.1.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + mkdirp "^0.5.1" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.8.0" + tsutils "^2.29.0" + +tsutils@^2.29.0: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== + dependencies: + tslib "^1.8.1" + +tsutils@^3.7.0: + version "3.17.1" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" + integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== + dependencies: + tslib "^1.8.1" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +typescript@3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.2.tgz#105b0f1934119dde543ac8eb71af3a91009efe54" + integrity sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw== + +uglify-js@3.4.x: + version "3.4.10" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f" + integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw== + dependencies: + commander "~2.19.0" + source-map "~0.6.1" + +uglify-js@^3.1.4: + version "3.7.3" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.3.tgz#f918fce9182f466d5140f24bb0ff35c2d32dcc6a" + integrity sha512-7tINm46/3puUA4hCkKYo4Xdts+JDaVC9ZPRcG8Xw9R4nhO/gZgUM3TENq8IF4Vatk8qCig4MzP/c8G4u2BkVQg== + dependencies: + commander "~2.20.3" + source-map "~0.6.1" + +underscore-plus@1.x: + version "1.7.0" + resolved "https://registry.yarnpkg.com/underscore-plus/-/underscore-plus-1.7.0.tgz#107f1900c520ac1fefe4edec6580a7ff08a99d0f" + integrity sha512-A3BEzkeicFLnr+U/Q3EyWwJAQPbA19mtZZ4h+lLq3ttm9kn8WC4R3YpuJZEXmWdLjYP47Zc8aLZm9kwdv+zzvA== + dependencies: + underscore "^1.9.1" + +underscore@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" + integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== + +unicode-canonical-property-names-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" + integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== + +unicode-match-property-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" + integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== + dependencies: + unicode-canonical-property-names-ecmascript "^1.0.4" + unicode-property-aliases-ecmascript "^1.0.4" + +unicode-match-property-value-ecmascript@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" + integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== + +unicode-property-aliases-ecmascript@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" + integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= + +uniqs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" + integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= + +unique-filename@^1.1.0, unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== + dependencies: + imurmurhash "^0.1.4" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unquote@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" + integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +untildify@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-3.0.3.tgz#1e7b42b140bcfd922b22e70ca1265bfe3634c7c9" + integrity sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA== + +upath@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +upper-case@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +url-loader@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.1.2.tgz#b971d191b83af693c5e3fea4064be9e1f2d7f8d8" + integrity sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg== + dependencies: + loader-utils "^1.1.0" + mime "^2.0.3" + schema-utils "^1.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" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +url@0.11.0, url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util.promisify@1.0.0, util.promisify@^1.0.0, util.promisify@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= + dependencies: + inherits "2.0.1" + +util@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" + integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== + dependencies: + inherits "2.0.3" + +utila@^0.4.0, utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@^3.0.1, uuid@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" + integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== + +v8-compile-cache@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" + integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +vendors@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.3.tgz#a6467781abd366217c050f8202e7e50cc9eef8c0" + integrity sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vm-browserify@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== + +vue-class-component@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-7.1.0.tgz#b33efcb10e17236d684f70b1e96f1946ec793e87" + integrity sha512-G9152NzUkz0i0xTfhk0Afc8vzdXxDR1pfN4dTwE72cskkgJtdXfrKBkMfGvDuxUh35U500g5Ve4xL8PEGdWeHg== + +vue-cli-plugin-element@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vue-cli-plugin-element/-/vue-cli-plugin-element-1.0.1.tgz#34e58fb65b36cf59afaf14f503288e5e578b1554" + integrity sha512-OJSOnJtn7f1v/8xX+MJae+RrE8WguhiiG9QTBx/MNOPXYsxqut6Ommo+ZD3raNc7eryhqdM2T/DlMfdvIKpCtw== + +vue-cli-plugin-style-resources-loader@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/vue-cli-plugin-style-resources-loader/-/vue-cli-plugin-style-resources-loader-0.1.4.tgz#6087a86132ea8125aa89e5f8e0a978fbc8cf6f59" + integrity sha512-aK2TyVThZO6oVPZJpCckha8ALcg1RXKYLNqtuy1fNv+24x6zGWKpSK+FHAo47B7yeRlFS9DbKo+cvUFMqWaZ7Q== + +vue-eslint-parser@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz#c268c96c6d94cfe3d938a5f7593959b0ca3360d1" + integrity sha512-ZezcU71Owm84xVF6gfurBQUGg8WQ+WZGxgDEQu1IHFBZNx7BFZg3L1yHxrCBNNwbwFtE1GuvfJKMtb6Xuwc/Bw== + dependencies: + debug "^3.1.0" + eslint-scope "^3.7.1" + eslint-visitor-keys "^1.0.0" + espree "^3.5.2" + esquery "^1.0.0" + lodash "^4.17.4" + +vue-eslint-parser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-5.0.0.tgz#00f4e4da94ec974b821a26ff0ed0f7a78402b8a1" + integrity sha512-JlHVZwBBTNVvzmifwjpZYn0oPWH2SgWv5dojlZBsrhablDu95VFD+hriB1rQGwbD+bms6g+rAFhQHk6+NyiS6g== + dependencies: + debug "^4.1.0" + eslint-scope "^4.0.0" + eslint-visitor-keys "^1.0.0" + espree "^4.1.0" + esquery "^1.0.1" + lodash "^4.17.11" + +vue-hot-reload-api@^2.3.0: + version "2.3.4" + resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2" + integrity sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog== + +vue-jest@^3.0.4: + version "3.0.5" + resolved "https://registry.yarnpkg.com/vue-jest/-/vue-jest-3.0.5.tgz#d6f124b542dcbff207bf9296c19413f4c40b70c9" + integrity sha512-xWDxde91pDqYBGDlODENZ3ezPgw+IQFoVDtf+5Awlg466w3KvMSqWzs8PxcTeTr+wmAHi0j+a+Lm3R7aUJa1jA== + dependencies: + babel-plugin-transform-es2015-modules-commonjs "^6.26.0" + chalk "^2.1.0" + extract-from-css "^0.4.4" + find-babel-config "^1.1.0" + js-beautify "^1.6.14" + node-cache "^4.1.1" + object-assign "^4.1.1" + source-map "^0.5.6" + tsconfig "^7.0.0" + vue-template-es2015-compiler "^1.6.0" + +vue-loader@^15.7.0: + version "15.8.3" + resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.8.3.tgz#857cb9e30eb5fc25e66db48dce7e4f768602a23c" + integrity sha512-yFksTFbhp+lxlm92DrKdpVIWMpranXnTEuGSc0oW+Gk43M9LWaAmBTnfj5+FCdve715mTHvo78IdaXf5TbiTJg== + dependencies: + "@vue/component-compiler-utils" "^3.1.0" + hash-sum "^1.0.2" + loader-utils "^1.1.0" + vue-hot-reload-api "^2.3.0" + vue-style-loader "^4.1.0" + +vue-property-decorator@^8.2.2: + version "8.3.0" + resolved "https://registry.yarnpkg.com/vue-property-decorator/-/vue-property-decorator-8.3.0.tgz#536f027dc7d626f37c8d85a2dc02f0a6cb979440" + integrity sha512-77YtTbZHd5CNiPzbqv51kEgL48yvD2dUDfF28vEyw3MbQ9bBAb/tDyFzskcqjNRbWyXk1vq4oM2CK/LfhxuIBg== + dependencies: + vue-class-component "^7.1.0" + +vue-router@^3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.1.3.tgz#e6b14fabc0c0ee9fda0e2cbbda74b350e28e412b" + integrity sha512-8iSa4mGNXBjyuSZFCCO4fiKfvzqk+mhL0lnKuGcQtO1eoj8nq3CmbEG8FwK5QqoqwDgsjsf1GDuisDX4cdb/aQ== + +vue-style-loader@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.2.tgz#dedf349806f25ceb4e64f3ad7c0a44fba735fcf8" + integrity sha512-0ip8ge6Gzz/Bk0iHovU9XAUQaFt/G2B61bnWa2tCcqqdgfHs1lF9xXorFbE55Gmy92okFT+8bfmySuUOu13vxQ== + dependencies: + hash-sum "^1.0.2" + loader-utils "^1.0.2" + +vue-svgicon@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/vue-svgicon/-/vue-svgicon-3.2.6.tgz#b459b8025741bb6b4dc9c5632ee4ce3ec6350b89" + integrity sha512-ZmX+s4H3lJkJLL8+Fg5a8N0tLQow1B5cVYo4yBr5777gS6uFSw6/ZXBITmtlb+nhcCgWhULA43hMTH5b9goIHA== + dependencies: + camelcase "^5.2.0" + colors "^1.3.0" + fs-plus "^3.0.2" + glob "^7.1.2" + svgo "^1.0.5" + tslib "^1.9.3" + yargs "^12.0.1" + +vue-template-compiler@^2.6.10: + version "2.6.11" + resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz#c04704ef8f498b153130018993e56309d4698080" + integrity sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA== + dependencies: + de-indent "^1.0.2" + he "^1.1.0" + +vue-template-es2015-compiler@^1.6.0, vue-template-es2015-compiler@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825" + integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw== + +vue@^2.6.10: + version "2.6.11" + resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.11.tgz#76594d877d4b12234406e84e35275c6d514125c5" + integrity sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ== + +vuex-class@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/vuex-class/-/vuex-class-0.3.2.tgz#c7e96a076c1682137d4d23a8dcfdc63f220e17a8" + integrity sha512-m0w7/FMsNcwJgunJeM+wcNaHzK2KX1K1rw2WUQf7Q16ndXHo7pflRyOV/E8795JO/7fstyjH3EgqBI4h4n4qXQ== + +vuex-module-decorators@^0.10.1: + version "0.10.1" + resolved "https://registry.yarnpkg.com/vuex-module-decorators/-/vuex-module-decorators-0.10.1.tgz#a34ebf7c2aec701d19ae60dea0a8f1620825fd9e" + integrity sha512-OBvHEeer8uT///oTWqgf7kPe0j3hb9IC9ahdk41S4gBHhn5YUHpyXdry2OXp77zPzQ1P6wTY+Ju+/VUcU5mhzA== + +vuex@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.1.2.tgz#a2863f4005aa73f2587e55c3fadf3f01f69c7d4d" + integrity sha512-ha3jNLJqNhhrAemDXcmMJMKf1Zu4sybMPr9KxJIuOpVcsDQlTBYLLladav2U+g1AvdYDG5Gs0xBTb0M5pXXYFQ== + +w3c-hr-time@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" + integrity sha1-gqwr/2PZUOqeMYmlimViX+3xkEU= + dependencies: + browser-process-hrtime "^0.1.2" + +walker@^1.0.7, walker@~1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= + dependencies: + makeerror "1.0.x" + +watch@~0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/watch/-/watch-0.18.0.tgz#28095476c6df7c90c963138990c0a5423eb4b986" + integrity sha1-KAlUdsbffJDJYxOJkMClQj60uYY= + dependencies: + exec-sh "^0.2.0" + minimist "^1.2.0" + +watchpack@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" + integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA== + dependencies: + chokidar "^2.0.2" + graceful-fs "^4.1.2" + neo-async "^2.5.0" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= + dependencies: + defaults "^1.0.3" + +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + +webpack-bundle-analyzer@^3.3.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.6.0.tgz#39b3a8f829ca044682bc6f9e011c95deb554aefd" + integrity sha512-orUfvVYEfBMDXgEKAKVvab5iQ2wXneIEorGNsyuOyVYpjYrI7CUOhhXNDd3huMwQ3vNNWWlGP+hzflMFYNzi2g== + dependencies: + acorn "^6.0.7" + acorn-walk "^6.1.1" + bfj "^6.1.1" + chalk "^2.4.1" + commander "^2.18.0" + ejs "^2.6.1" + express "^4.16.3" + filesize "^3.6.1" + gzip-size "^5.0.0" + lodash "^4.17.15" + mkdirp "^0.5.1" + opener "^1.5.1" + ws "^6.0.0" + +webpack-chain@^4.11.0: + version "4.12.1" + resolved "https://registry.yarnpkg.com/webpack-chain/-/webpack-chain-4.12.1.tgz#6c8439bbb2ab550952d60e1ea9319141906c02a6" + integrity sha512-BCfKo2YkDe2ByqkEWe1Rw+zko4LsyS75LVr29C6xIrxAg9JHJ4pl8kaIZ396SUSNp6b4815dRZPSTAS8LlURRQ== + dependencies: + deepmerge "^1.5.2" + javascript-stringify "^1.6.0" + +webpack-dev-middleware@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3" + integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw== + dependencies: + memory-fs "^0.4.1" + mime "^2.4.4" + mkdirp "^0.5.1" + range-parser "^1.2.1" + webpack-log "^2.0.0" + +webpack-dev-server@^3.4.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.10.1.tgz#1ff3e5cccf8e0897aa3f5909c654e623f69b1c0e" + integrity sha512-AGG4+XrrXn4rbZUueyNrQgO4KGnol+0wm3MPdqGLmmA+NofZl3blZQKxZ9BND6RDNuvAK9OMYClhjOSnxpWRoA== + dependencies: + ansi-html "0.0.7" + bonjour "^3.5.0" + chokidar "^2.1.8" + compression "^1.7.4" + connect-history-api-fallback "^1.6.0" + debug "^4.1.1" + del "^4.1.1" + express "^4.17.1" + html-entities "^1.2.1" + http-proxy-middleware "0.19.1" + import-local "^2.0.0" + internal-ip "^4.3.0" + ip "^1.1.5" + is-absolute-url "^3.0.3" + killable "^1.0.1" + loglevel "^1.6.6" + opn "^5.5.0" + p-retry "^3.0.1" + portfinder "^1.0.25" + schema-utils "^1.0.0" + selfsigned "^1.10.7" + semver "^6.3.0" + serve-index "^1.9.1" + sockjs "0.3.19" + sockjs-client "1.4.0" + spdy "^4.0.1" + strip-ansi "^3.0.1" + supports-color "^6.1.0" + url "^0.11.0" + webpack-dev-middleware "^3.7.2" + webpack-log "^2.0.0" + ws "^6.2.1" + yargs "12.0.5" + +webpack-log@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" + integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== + dependencies: + ansi-colors "^3.0.0" + uuid "^3.3.2" + +webpack-merge@^4.2.1: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== + dependencies: + lodash "^4.17.15" + +webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack@^4.0.0, webpack@^4.39.3: + version "4.41.5" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.5.tgz#3210f1886bce5310e62bb97204d18c263341b77c" + integrity sha512-wp0Co4vpyumnp3KlkmpM5LWuzvZYayDwM2n17EHFr4qxBBbRokC7DJawPJC7TfSFZ9HZ6GsdH40EBj4UV0nmpw== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/wasm-edit" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + acorn "^6.2.1" + ajv "^6.10.2" + ajv-keywords "^3.4.1" + chrome-trace-event "^1.0.2" + enhanced-resolve "^4.1.0" + eslint-scope "^4.0.3" + json-parse-better-errors "^1.0.2" + loader-runner "^2.4.0" + loader-utils "^1.2.3" + memory-fs "^0.4.1" + micromatch "^3.1.10" + mkdirp "^0.5.1" + neo-async "^2.6.1" + node-libs-browser "^2.2.1" + schema-utils "^1.0.0" + tapable "^1.1.3" + terser-webpack-plugin "^1.4.3" + watchpack "^1.6.0" + webpack-sources "^1.4.1" + +websocket-driver@>=0.5.1: + version "0.7.3" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9" + integrity sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg== + dependencies: + http-parser-js ">=0.4.0 <0.4.11" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" + integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== + +whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^6.4.1: + version "6.5.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" + integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@^1.2.12, which@^1.2.9, which@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= + +workbox-background-sync@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-3.6.3.tgz#6609a0fac9eda336a7c52e6aa227ba2ae532ad94" + integrity sha512-ypLo0B6dces4gSpaslmDg5wuoUWrHHVJfFWwl1udvSylLdXvnrfhFfriCS42SNEe5lsZtcNZF27W/SMzBlva7Q== + dependencies: + workbox-core "^3.6.3" + +workbox-broadcast-cache-update@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/workbox-broadcast-cache-update/-/workbox-broadcast-cache-update-3.6.3.tgz#3f5dff22ada8c93e397fb38c1dc100606a7b92da" + integrity sha512-pJl4lbClQcvp0SyTiEw0zLSsVYE1RDlCPtpKnpMjxFtu8lCFTAEuVyzxp9w7GF4/b3P4h5nyQ+q7V9mIR7YzGg== + dependencies: + workbox-core "^3.6.3" + +workbox-build@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-3.6.3.tgz#77110f9f52dc5d82fa6c1c384c6f5e2225adcbd8" + integrity sha512-w0clZ/pVjL8VXy6GfthefxpEXs0T8uiRuopZSFVQ8ovfbH6c6kUpEh6DcYwm/Y6dyWPiCucdyAZotgjz+nRz8g== + dependencies: + babel-runtime "^6.26.0" + common-tags "^1.4.0" + fs-extra "^4.0.2" + glob "^7.1.2" + joi "^11.1.1" + lodash.template "^4.4.0" + pretty-bytes "^4.0.2" + stringify-object "^3.2.2" + strip-comments "^1.0.2" + workbox-background-sync "^3.6.3" + workbox-broadcast-cache-update "^3.6.3" + workbox-cache-expiration "^3.6.3" + workbox-cacheable-response "^3.6.3" + workbox-core "^3.6.3" + workbox-google-analytics "^3.6.3" + workbox-navigation-preload "^3.6.3" + workbox-precaching "^3.6.3" + workbox-range-requests "^3.6.3" + workbox-routing "^3.6.3" + workbox-strategies "^3.6.3" + workbox-streams "^3.6.3" + workbox-sw "^3.6.3" + +workbox-cache-expiration@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/workbox-cache-expiration/-/workbox-cache-expiration-3.6.3.tgz#4819697254a72098a13f94b594325a28a1e90372" + integrity sha512-+ECNph/6doYx89oopO/UolYdDmQtGUgo8KCgluwBF/RieyA1ZOFKfrSiNjztxOrGJoyBB7raTIOlEEwZ1LaHoA== + dependencies: + workbox-core "^3.6.3" + +workbox-cacheable-response@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-3.6.3.tgz#869f1a68fce9063f6869ddbf7fa0a2e0a868b3aa" + integrity sha512-QpmbGA9SLcA7fklBLm06C4zFg577Dt8u3QgLM0eMnnbaVv3rhm4vbmDpBkyTqvgK/Ly8MBDQzlXDtUCswQwqqg== + dependencies: + workbox-core "^3.6.3" + +workbox-core@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-3.6.3.tgz#69abba70a4f3f2a5c059295a6f3b7c62bd00e15c" + integrity sha512-cx9cx0nscPkIWs8Pt98HGrS9/aORuUcSkWjG25GqNWdvD/pSe7/5Oh3BKs0fC+rUshCiyLbxW54q0hA+GqZeSQ== + +workbox-google-analytics@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-3.6.3.tgz#99df2a3d70d6e91961e18a6752bac12e91fbf727" + integrity sha512-RQBUo/6SXtIaQTRFj4RQZ9e1gAl7D8oS5S+Hi173Kk70/BgJjzPwXpC5A249Jv5YfkCOLMQCeF9A27BiD0b0ig== + dependencies: + workbox-background-sync "^3.6.3" + workbox-core "^3.6.3" + workbox-routing "^3.6.3" + workbox-strategies "^3.6.3" + +workbox-navigation-preload@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-3.6.3.tgz#a2c34eb7c17e7485b795125091215f757b3c4964" + integrity sha512-dd26xTX16DUu0i+MhqZK/jQXgfIitu0yATM4jhRXEmpMqQ4MxEeNvl2CgjDMOHBnCVMax+CFZQWwxMx/X/PqCw== + dependencies: + workbox-core "^3.6.3" + +workbox-precaching@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-3.6.3.tgz#5341515e9d5872c58ede026a31e19bafafa4e1c1" + integrity sha512-aBqT66BuMFviPTW6IpccZZHzpA8xzvZU2OM1AdhmSlYDXOJyb1+Z6blVD7z2Q8VNtV1UVwQIdImIX+hH3C3PIw== + dependencies: + workbox-core "^3.6.3" + +workbox-range-requests@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-3.6.3.tgz#3cc21cba31f2dd8c43c52a196bcc8f6cdbcde803" + integrity sha512-R+yLWQy7D9aRF9yJ3QzwYnGFnGDhMUij4jVBUVtkl67oaVoP1ymZ81AfCmfZro2kpPRI+vmNMfxxW531cqdx8A== + dependencies: + workbox-core "^3.6.3" + +workbox-routing@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-3.6.3.tgz#659cd8f9274986cfa98fda0d050de6422075acf7" + integrity sha512-bX20i95OKXXQovXhFOViOK63HYmXvsIwZXKWbSpVeKToxMrp0G/6LZXnhg82ijj/S5yhKNRf9LeGDzaqxzAwMQ== + dependencies: + workbox-core "^3.6.3" + +workbox-strategies@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-3.6.3.tgz#11a0dc249a7bc23d3465ec1322d28fa6643d64a0" + integrity sha512-Pg5eulqeKet2y8j73Yw6xTgLdElktcWExGkzDVCGqfV9JCvnGuEpz5eVsCIK70+k4oJcBCin9qEg3g3CwEIH3g== + dependencies: + workbox-core "^3.6.3" + +workbox-streams@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-3.6.3.tgz#beaea5d5b230239836cc327b07d471aa6101955a" + integrity sha512-rqDuS4duj+3aZUYI1LsrD2t9hHOjwPqnUIfrXSOxSVjVn83W2MisDF2Bj+dFUZv4GalL9xqErcFW++9gH+Z27w== + dependencies: + workbox-core "^3.6.3" + +workbox-sw@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-3.6.3.tgz#278ea4c1831b92bbe2d420da8399176c4b2789ff" + integrity sha512-IQOUi+RLhvYCiv80RP23KBW/NTtIvzvjex28B8NW1jOm+iV4VIu3VXKXTA6er5/wjjuhmtB28qEAUqADLAyOSg== + +workbox-webpack-plugin@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-3.6.3.tgz#a807bb891b4e4e3c808df07e58f17de2d5ba6182" + integrity sha512-RwmKjc7HFHUFHoOlKoZUq9349u0QN3F8W5tZZU0vc1qsBZDINWXRiIBCAKvo/Njgay5sWz7z4I2adnyTo97qIQ== + dependencies: + babel-runtime "^6.26.0" + json-stable-stringify "^1.0.1" + workbox-build "^3.6.3" + +worker-farm@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" + integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== + dependencies: + errno "~0.1.7" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-file-atomic@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.1.tgz#d0b05463c188ae804396fd5ab2a370062af87529" + integrity sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + +write-file-atomic@^2.1.0: + version "2.4.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" + integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +write@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" + integrity sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c= + dependencies: + mkdirp "^0.5.1" + +ws@^5.2.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + dependencies: + async-limiter "~1.0.0" + +ws@^6.0.0, ws@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" + integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== + dependencies: + async-limiter "~1.0.0" + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +xtend@^4.0.0, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= + +"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yargs-parser@10.x: + version "10.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" + integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ== + dependencies: + camelcase "^4.1.0" + +yargs-parser@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" + integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^13.1.1: + version "13.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" + integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^16.1.0: + version "16.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-16.1.0.tgz#73747d53ae187e7b8dbe333f95714c76ea00ecf1" + integrity sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" + integrity sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc= + dependencies: + camelcase "^4.1.0" + +yargs@12.0.5, yargs@^12.0.1: + version "12.0.5" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" + integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== + dependencies: + cliui "^4.0.0" + decamelize "^1.2.0" + find-up "^3.0.0" + get-caller-file "^1.0.1" + os-locale "^3.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1 || ^4.0.0" + yargs-parser "^11.1.1" + +yargs@^11.0.0: + version "11.1.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.1.tgz#5052efe3446a4df5ed669c995886cc0f13702766" + integrity sha512-PRU7gJrJaXv3q3yQZ/+/X6KBswZiaQ+zOmdprZcouPYtQgvNU35i+68M4b1ZHLZtYFT5QObFLV+ZkmJYcwKdiw== + dependencies: + cliui "^4.0.0" + decamelize "^1.1.1" + find-up "^2.1.0" + get-caller-file "^1.0.1" + os-locale "^3.1.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1" + yargs-parser "^9.0.2" + +yargs@^13.3.0: + version "13.3.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" + integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.1" + +yargs@^15.0.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.1.0.tgz#e111381f5830e863a89550bd4b136bb6a5f37219" + integrity sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^16.1.0" + +yauzl@2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + +yauzl@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" + integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU= + dependencies: + fd-slicer "~1.0.1" + +yorkie@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yorkie/-/yorkie-2.0.0.tgz#92411912d435214e12c51c2ae1093e54b6bb83d9" + integrity sha512-jcKpkthap6x63MB4TxwCyuIGkV0oYP/YRyuQU5UO0Yz/E/ZAu+653/uov+phdmO54n6BcvFRyyt0RRrWdN2mpw== + dependencies: + execa "^0.8.0" + is-ci "^1.0.10" + normalize-path "^1.0.0" + strip-indent "^2.0.0"