Compare commits
52 commits
main
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1dc53de30d | ||
|
|
ac2d12d95f | ||
|
|
aa7b3fd617 | ||
|
|
94b541c758 | ||
|
|
45ef98b163 | ||
|
|
35eb7d4152 | ||
|
|
7c23521cb8 | ||
|
|
8a8df0a8cb | ||
|
|
e54f62a0e6 | ||
|
|
0bdb416594 | ||
|
|
3484da2f60 | ||
|
|
2f164708e7 | ||
|
|
c66296a050 | ||
|
|
a1cf508cb3 | ||
|
|
38594319f0 | ||
|
|
c963ec5e91 | ||
|
|
c04090a778 | ||
|
|
fc48ba4149 | ||
|
|
04db322e54 | ||
|
|
c9ea3651db | ||
|
|
1dc63bf88b | ||
|
|
2fb7b3d542 | ||
|
|
57a21eb31d | ||
|
|
e9a1a8c4eb | ||
|
|
14191c1de4 | ||
|
|
7e52c97d62 | ||
|
|
1cdb9bd06d | ||
|
|
e7ad4c8bd0 | ||
|
|
29d69e5b24 | ||
|
|
2e5517509b | ||
|
|
c95a4f6770 | ||
|
|
8af7d1a263 | ||
|
|
332cbb3516 | ||
|
|
1b564f53d5 | ||
|
|
1aa3b4b4e5 | ||
|
|
d531b5da7e | ||
|
|
e173591d88 | ||
|
|
359ecc85af | ||
|
|
f0e8100283 | ||
|
|
251d727375 | ||
|
|
b00a90484e | ||
|
|
ce0686eec3 | ||
|
|
34232ed23c | ||
|
|
954bfbf8f7 | ||
|
|
c0197cdfeb | ||
|
|
12ab110e0e | ||
|
|
f48808f42e | ||
|
|
b080a98764 | ||
|
|
dd885c65ac | ||
|
|
0ad2836650 | ||
|
|
20b76a0ed9 | ||
|
|
884f3499ef |
99 changed files with 2152 additions and 1714 deletions
1
.github/workflows/patch-build.yaml
vendored
1
.github/workflows/patch-build.yaml
vendored
|
|
@ -83,6 +83,7 @@ jobs:
|
|||
[ -d $MSAAS_REPO_FOLDER ] || {
|
||||
git clone -b dev --recursive https://x-access-token:$MSAAS_REPO_CLONE_TOKEN@$MSAAS_REPO_URL $MSAAS_REPO_FOLDER
|
||||
cd $MSAAS_REPO_FOLDER
|
||||
git log -1
|
||||
cd openreplay && git fetch origin && git checkout main # This have to be changed to specific tag
|
||||
git log -1
|
||||
cd $MSAAS_REPO_FOLDER
|
||||
|
|
|
|||
|
|
@ -457,12 +457,6 @@ def set_password_invitation(user_id, new_password):
|
|||
user = update(tenant_id=-1, user_id=user_id, changes=changes)
|
||||
r = authenticate(user['email'], new_password)
|
||||
|
||||
tenant_id = r.pop("tenantId")
|
||||
r["limits"] = {
|
||||
"teamMember": -1,
|
||||
"projects": -1,
|
||||
"metadata": metadata.get_remaining_metadata_with_count(tenant_id)}
|
||||
|
||||
return {
|
||||
"jwt": r.pop("jwt"),
|
||||
"refreshToken": r.pop("refreshToken"),
|
||||
|
|
@ -470,10 +464,7 @@ def set_password_invitation(user_id, new_password):
|
|||
"spotJwt": r.pop("spotJwt"),
|
||||
"spotRefreshToken": r.pop("spotRefreshToken"),
|
||||
"spotRefreshTokenMaxAge": r.pop("spotRefreshTokenMaxAge"),
|
||||
'data': {
|
||||
"scopeState": scope.get_scope(-1),
|
||||
"user": r
|
||||
}
|
||||
**r
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -129,13 +129,13 @@ def add_edit(tenant_id, data: schemas.WebhookSchema, replace_none=None):
|
|||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"name already exists.")
|
||||
if data.webhook_id is not None:
|
||||
return update(tenant_id=tenant_id, webhook_id=data.webhook_id,
|
||||
changes={"endpoint": data.endpoint.unicode_string(),
|
||||
changes={"endpoint": data.endpoint,
|
||||
"authHeader": data.auth_header,
|
||||
"name": data.name},
|
||||
replace_none=replace_none)
|
||||
else:
|
||||
return add(tenant_id=tenant_id,
|
||||
endpoint=data.endpoint.unicode_string(),
|
||||
endpoint=data.endpoint,
|
||||
auth_header=data.auth_header,
|
||||
name=data.name,
|
||||
replace_none=replace_none)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#!/bin/sh
|
||||
export TZ=UTC
|
||||
|
||||
uvicorn app:app --host 0.0.0.0 --port $LISTEN_PORT --proxy-headers --log-level ${S_LOGLEVEL:-warning}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#!/bin/sh
|
||||
export TZ=UTC
|
||||
export ASSIST_KEY=ignore
|
||||
uvicorn app:app --host 0.0.0.0 --port 8888 --log-level ${S_LOGLEVEL:-warning}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#!/bin/zsh
|
||||
export TZ=UTC
|
||||
|
||||
uvicorn app_alerts:app --reload --port 8888 --log-level ${S_LOGLEVEL:-warning}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
#!/bin/zsh
|
||||
export TZ=UTC
|
||||
|
||||
uvicorn app:app --reload --log-level ${S_LOGLEVEL:-warning}
|
||||
|
|
@ -211,7 +211,8 @@ class IssueTrackingJiraSchema(IssueTrackingIntegration):
|
|||
|
||||
class WebhookSchema(BaseModel):
|
||||
webhook_id: Optional[int] = Field(default=None)
|
||||
endpoint: AnyHttpUrl = Field(...)
|
||||
processed_endpoint: AnyHttpUrl = Field(..., alias="endpoint")
|
||||
endpoint: Optional[str] = Field(default=None, doc_hidden=True)
|
||||
auth_header: Optional[str] = Field(default=None)
|
||||
name: str = Field(default="", max_length=100, pattern=NAME_PATTERN)
|
||||
|
||||
|
|
@ -754,6 +755,8 @@ class SessionsSearchPayloadSchema(_TimedSchema, _PaginatedSchema):
|
|||
for f in values.get("filters", []):
|
||||
vals = []
|
||||
for v in f.get("value", []):
|
||||
if f.get("type", "") == FilterType.DURATION.value and v is None:
|
||||
v = 0
|
||||
if v is not None and (f.get("type", "") != FilterType.DURATION.value
|
||||
or str(v).isnumeric()):
|
||||
vals.append(v)
|
||||
|
|
@ -1020,33 +1023,51 @@ class CardSessionsSchema(_TimedSchema, _PaginatedSchema):
|
|||
|
||||
return self
|
||||
|
||||
# We don't need this as the UI is expecting filters to override the full series' filters
|
||||
# @model_validator(mode="after")
|
||||
# def __merge_out_filters_with_series(self):
|
||||
# for f in self.filters:
|
||||
# for s in self.series:
|
||||
# found = False
|
||||
#
|
||||
# if f.is_event:
|
||||
# sub = s.filter.events
|
||||
# else:
|
||||
# sub = s.filter.filters
|
||||
#
|
||||
# for e in sub:
|
||||
# if f.type == e.type and f.operator == e.operator:
|
||||
# found = True
|
||||
# if f.is_event:
|
||||
# # If extra event: append value
|
||||
# for v in f.value:
|
||||
# if v not in e.value:
|
||||
# e.value.append(v)
|
||||
# else:
|
||||
# # If extra filter: override value
|
||||
# e.value = f.value
|
||||
# if not found:
|
||||
# sub.append(f)
|
||||
#
|
||||
# self.filters = []
|
||||
#
|
||||
# return self
|
||||
|
||||
# UI is expecting filters to override the full series' filters
|
||||
@model_validator(mode="after")
|
||||
def __merge_out_filters_with_series(self):
|
||||
for f in self.filters:
|
||||
for s in self.series:
|
||||
found = False
|
||||
|
||||
def __override_series_filters_with_outer_filters(self):
|
||||
if len(self.filters) > 0:
|
||||
events = []
|
||||
filters = []
|
||||
for f in self.filters:
|
||||
if f.is_event:
|
||||
sub = s.filter.events
|
||||
events.append(f)
|
||||
else:
|
||||
sub = s.filter.filters
|
||||
|
||||
for e in sub:
|
||||
if f.type == e.type and f.operator == e.operator:
|
||||
found = True
|
||||
if f.is_event:
|
||||
# If extra event: append value
|
||||
for v in f.value:
|
||||
if v not in e.value:
|
||||
e.value.append(v)
|
||||
else:
|
||||
# If extra filter: override value
|
||||
e.value = f.value
|
||||
if not found:
|
||||
sub.append(f)
|
||||
|
||||
filters.append(f)
|
||||
for s in self.series:
|
||||
s.filter.events = events
|
||||
s.filter.filters = filters
|
||||
self.filters = []
|
||||
|
||||
return self
|
||||
|
||||
|
||||
|
|
@ -1357,6 +1378,7 @@ class LiveFilterType(str, Enum):
|
|||
USER_BROWSER = FilterType.USER_BROWSER.value
|
||||
USER_DEVICE = FilterType.USER_DEVICE.value
|
||||
USER_COUNTRY = FilterType.USER_COUNTRY.value
|
||||
USER_CITY = FilterType.USER_CITY.value
|
||||
USER_STATE = FilterType.USER_STATE.value
|
||||
USER_ID = FilterType.USER_ID.value
|
||||
USER_ANONYMOUS_ID = FilterType.USER_ANONYMOUS_ID.value
|
||||
|
|
|
|||
|
|
@ -3,17 +3,11 @@ module openreplay/backend
|
|||
go 1.23
|
||||
|
||||
require (
|
||||
cloud.google.com/go/logging v1.9.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.2.0
|
||||
github.com/DataDog/datadog-api-client-go/v2 v2.30.0
|
||||
github.com/Masterminds/semver v1.5.0
|
||||
github.com/andybalholm/brotli v1.1.0
|
||||
github.com/aws/aws-sdk-go v1.44.98
|
||||
github.com/btcsuite/btcutil v1.0.2
|
||||
github.com/confluentinc/confluent-kafka-go/v2 v2.4.0
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/docker/distribution v2.8.3+incompatible
|
||||
github.com/elastic/go-elasticsearch/v7 v7.13.1
|
||||
github.com/elastic/go-elasticsearch/v8 v8.13.0
|
||||
|
|
@ -28,7 +22,6 @@ require (
|
|||
github.com/jackc/pgx/v4 v4.18.2
|
||||
github.com/klauspost/compress v1.17.7
|
||||
github.com/klauspost/pgzip v1.2.5
|
||||
github.com/lib/pq v1.10.2
|
||||
github.com/oschwald/maxminddb-golang v1.7.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.16.0
|
||||
|
|
@ -37,32 +30,18 @@ require (
|
|||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
|
||||
github.com/ua-parser/uap-go v0.0.0-20200325213135-e1c09f13e2fe
|
||||
go.uber.org/zap v1.17.0
|
||||
golang.org/x/net v0.23.0
|
||||
google.golang.org/api v0.169.0
|
||||
golang.org/x/net v0.29.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.112.1 // indirect
|
||||
cloud.google.com/go/compute v1.25.1 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v1.1.7 // indirect
|
||||
cloud.google.com/go/longrunning v0.5.6 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0 // indirect
|
||||
github.com/DataDog/zstd v1.5.2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/elastic/elastic-transport-go/v8 v8.5.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.2 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
|
|
@ -70,34 +49,26 @@ require (
|
|||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/puddle v1.3.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/paulmach/orb v0.7.1 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
||||
github.com/prometheus/client_model v0.4.0 // indirect
|
||||
github.com/prometheus/common v0.42.0 // indirect
|
||||
github.com/prometheus/procfs v0.10.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.22.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/crypto v0.21.0 // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/oauth2 v0.17.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240325203815-454cdb8f5daa // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
|
||||
google.golang.org/grpc v1.62.1 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
|
|
|||
491
backend/go.sum
491
backend/go.sum
|
|
@ -1,40 +1,4 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM=
|
||||
cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4=
|
||||
cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU=
|
||||
cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/iam v1.1.7 h1:z4VHOhwKLF/+UYXAJDFwGtNF0b6gjsW1Pk9Ml0U/IoM=
|
||||
cloud.google.com/go/iam v1.1.7/go.mod h1:J4PMPg8TtyurAUvSmPj8FF3EDgY1SPRZxcUGrn7WXGA=
|
||||
cloud.google.com/go/logging v1.9.0 h1:iEIOXFO9EmSiTjDmfpbRjOxECO7R8C7b8IXUGOj7xZw=
|
||||
cloud.google.com/go/logging v1.9.0/go.mod h1:1Io0vnZv4onoUnsVUQY3HZ3Igb1nBchky0A0y7BBBhE=
|
||||
cloud.google.com/go/longrunning v0.5.6 h1:xAe8+0YaWoCKr9t1+aWe+OeQgN/iJK1fEgZSXmjuEaE=
|
||||
cloud.google.com/go/longrunning v0.5.6/go.mod h1:vUaDrWYOMKRuhiv6JBnn49YxCPz2Ayn9GqyjaBT8/mA=
|
||||
cloud.google.com/go/storage v1.38.0 h1:Az68ZRGlnNTpIBbLjSMIV2BDcwwXYlRlQzis0llkpJg=
|
||||
cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHXlpHmvFJoY=
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0 h1:rTnT/Jrcm+figWlYz4Ixzt0SJVR2cMC8lvZcimipiEY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2 h1:uqM+VoHjVH6zdlkLF2b6O0ZANcHoj3rO0PoQ3jglUJA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2/go.mod h1:twTKAa1E6hLmSDjLhaCkbTMQKc7p/rNLU40rLxGEOCI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0 h1:leh5DwKv6Ihwi+h60uHtn6UWAxBbZ0q8DwQVMzf61zw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0 h1:u/LLAOFgsMv7HmNL4Qufg58y+qElGOt5qv0z1mURkRY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0/go.mod h1:2e8rMJtl2+2j+HXbTBwnyGpm5Nou7KhvSfxOq8JpTag=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v0.9.0 h1:UE9n9rkJF62ArLb1F3DEjRt8O3jLwMWdSoypKV4f3MU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v0.9.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.2.0 h1:dj00TDKY+xwuTJdbpspCSmTLFyWzRJerTHwaBxut1C0=
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.2.0/go.mod h1:8f2XZUi7XoeU+uPIytSi1cvx8fmJxi7vIgqpvYTF1+o=
|
||||
github.com/DataDog/datadog-api-client-go/v2 v2.30.0 h1:WHAo6RA8CqAzaUh3dERqz/n6SuG2GJ/WthBkccn0MIQ=
|
||||
github.com/DataDog/datadog-api-client-go/v2 v2.30.0/go.mod h1:QKOu6vscsh87fMY1lHfLEmNSunyXImj8BUaUWJXOehc=
|
||||
github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8=
|
||||
|
|
@ -42,45 +6,13 @@ github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwS
|
|||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8=
|
||||
github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w=
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/aws/aws-sdk-go v1.44.98 h1:fX+NxebSdO/9T6DTNOLhpC+Vv6RNkKRfsMg0a7o/yBo=
|
||||
github.com/aws/aws-sdk-go v1.44.98/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.6 h1:Y773UK7OBqhzi5VDXMi1zVGsoj+CVHs2eaC2bDsLwi0=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.6/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.16 h1:4r7gsCu8Ekwl5iJGE/GmspA2UifqySCCkyyyPFeWs3w=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.16/go.mod h1:XjM6lVbq7UgELp9NjXBrb1DQY/ownlWsvDhEQksemJc=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.16 h1:GgToSxaENX/1zXIGNFfiVk4hxryYJ5Vt4Mh8XLAL7Lc=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.16/go.mod h1:KP7aFJhfwPFgx9aoVYL2nYHjya5WBD98CWaadpgmnpY=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.24 h1:5qyqXASrX2zy5cTnoHHa4N2c3Lc94GH7gjnBP3GwKdU=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.24/go.mod h1:neYVaeKr5eT7BzwULuG2YbLhzWZ22lpjKdCybR7AXrQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30 h1:y+8n9AGDjikyXoMBTRaHHHSaFEB8267ykmvyPodJfys=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30/go.mod h1:LUBAO3zNXQjoONBKn/kR1y0Q4cj/D02Ts0uHYjcCQLM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24 h1:r+Kv+SEJquhAZXaJ7G4u44cIwXV3f8K+N482NNAzJZA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24/go.mod h1:gAuCezX/gob6BSMbItsSlMb6WZGV7K2+fWOvk8xBSto=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.31 h1:hf+Vhp5WtTdcSdE+yEcUz8L73sAzN0R+0jQv+Z51/mI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.31/go.mod h1:5zUjguZfG5qjhG9/wqmuyHRyUftl2B5Cp6NNxNC6kRA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24 h1:c5qGfdbCHav6viBwiyDns3OXqhqAbGjfIB4uVu2ayhk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24/go.mod h1:HMA4FZG6fyib+NDo5bpIxX1EhYjrAOveZJY2YR0xrNE=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.5 h1:bdKIX6SVF3nc3xJFw6Nf0igzS6Ff/louGq8Z6VP/3Hs=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.5/go.mod h1:vuWiaDB30M/QTC+lI3Wj6S/zb7tpUK2MSYgy3Guh2L0=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.5 h1:xLPZMyuZ4GuqRCIec/zWuIhRFPXh2UOJdLXBSi64ZWQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.5/go.mod h1:QjxpHmCwAg0ESGtPQnLIVp7SedTOBMYy+Slr3IfMKeI=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.6 h1:rIFn5J3yDoeuKCE9sESXqM5POTAhOP1du3bv/qTL+tE=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.6/go.mod h1:48WJ9l3dwP0GSHWGc5sFGGlCkuA82Mc2xnw+T6Q8aDw=
|
||||
github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
|
||||
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
|
|
@ -91,86 +23,27 @@ github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVa
|
|||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY=
|
||||
github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/compose-spec/compose-go/v2 v2.0.0-rc.2 h1:eJ01FpliL/02KvsaPyH1bSLbM1S70yWQUojHVRbyvy4=
|
||||
github.com/compose-spec/compose-go/v2 v2.0.0-rc.2/go.mod h1:IVsvFyGVhw4FASzUtlWNVaAOhYmakXAFY9IlZ7LAuD8=
|
||||
github.com/confluentinc/confluent-kafka-go/v2 v2.4.0 h1:NbOku86JJlsRJPJKE0snNsz6D1Qr4j5VR/lticrLZrY=
|
||||
github.com/confluentinc/confluent-kafka-go/v2 v2.4.0/go.mod h1:E1dEQy50ZLfqs7T9luxz0rLxaeFZJZE92XvApJOr/Rk=
|
||||
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
|
||||
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
||||
github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0=
|
||||
github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk=
|
||||
github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM=
|
||||
github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4=
|
||||
github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E=
|
||||
github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c=
|
||||
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
|
||||
github.com/docker/buildx v0.12.0-rc2.0.20231219140829-617f538cb315 h1:UZxx9xBADdf/9UmSdEUi+pdJoPKpgcf9QUAY5gEIYmY=
|
||||
github.com/docker/buildx v0.12.0-rc2.0.20231219140829-617f538cb315/go.mod h1:X8ZHhuW6ncwtoJ36TlU+gyaROTcBkTE01VHYmTStQCE=
|
||||
github.com/docker/cli v25.0.1+incompatible h1:mFpqnrS6Hsm3v1k7Wa/BO23oz0k121MTbTO1lpcGSkU=
|
||||
github.com/docker/cli v25.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/compose/v2 v2.24.3 h1:BVc1oDV7aQgksH64pDKTvcI95G36uJ+Mz9DGGBBoZeQ=
|
||||
github.com/docker/compose/v2 v2.24.3/go.mod h1:D8Nv9+juzD7xiMyyHJ7G2J/MOYiGBmb9SvdIW5+2zKo=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ=
|
||||
github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8=
|
||||
github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
|
||||
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/elastic/elastic-transport-go/v8 v8.5.0 h1:v5membAl7lvQgBTexPRDBO/RdnlQX+FM9fUVDyXxvH0=
|
||||
github.com/elastic/elastic-transport-go/v8 v8.5.0/go.mod h1:YLHer5cj0csTzNFXoNQ8qhtGY1GTvSqPnKWKaqQE3Hk=
|
||||
github.com/elastic/go-elasticsearch/v7 v7.13.1 h1:PaM3V69wPlnwR+ne50rSKKn0RNDYnnOFQcuGEI0ce80=
|
||||
github.com/elastic/go-elasticsearch/v7 v7.13.1/go.mod h1:OJ4wdbtDNk5g503kvlHLyErCgQwwzmDtaFC4XyOxXA4=
|
||||
github.com/elastic/go-elasticsearch/v8 v8.13.0 h1:YXPAWpvbYX0mWSNG9tnEpvs4h1stgMy5JUeKZECYYB8=
|
||||
github.com/elastic/go-elasticsearch/v8 v8.13.0/go.mod h1:DIn7HopJs4oZC/w0WoJR13uMUxtHeq92eI5bqv5CRfI=
|
||||
github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ=
|
||||
github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fsnotify/fsevents v0.1.1 h1:/125uxJvvoSDDBPen6yUZbil8J9ydKZnnl3TWWmvnkw=
|
||||
github.com/fsnotify/fsevents v0.1.1/go.mod h1:+d+hS27T6k5J8CRaPLKFgwKYcpS7GwW3Ule9+SC2ZRc=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo=
|
||||
github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||
github.com/getsentry/sentry-go v0.29.0 h1:YtWluuCFg9OfcqnaujpY918N/AhCCwarIDWOYSBAjCA=
|
||||
github.com/getsentry/sentry-go v0.29.0/go.mod h1:jhPesDAL0Q0W2+2YEuVOvdWmVtdsr1+jtBrlDEVWwLY=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
|
|
@ -178,108 +51,34 @@ github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3Bop
|
|||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=
|
||||
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
|
||||
github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
|
||||
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
|
||||
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0=
|
||||
github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=
|
||||
github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
||||
github.com/googleapis/gax-go/v2 v2.12.2 h1:mhN09QQW1jEWeMF74zGR81R30z4VJzjZsfkUhuHF+DA=
|
||||
github.com/googleapis/gax-go/v2 v2.12.2/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc=
|
||||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/in-toto/in-toto-golang v0.5.0 h1:hb8bgwr0M2hGdDsLjkJ3ZqJ8JFLL/tgYdAxF/XEFBbY=
|
||||
github.com/in-toto/in-toto-golang v0.5.0/go.mod h1:/Rq0IZHLV7Ku5gielPT4wPHJfH1GdHMCq8+WPxw8/BE=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
|
|
@ -336,17 +135,7 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
|
|||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
|
||||
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
|
||||
|
|
@ -360,117 +149,43 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
|||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
||||
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mkevac/debugcharts v0.0.0-20191222103121-ae1c48aa8615/go.mod h1:Ad7oeElCZqA1Ufj0U9/liOF4BtVepxRcTvr2ey7zTvM=
|
||||
github.com/moby/buildkit v0.13.0-beta1.0.20231219135447-957cb50df991 h1:r80LLQ91uOLxU1ElAvrB1o8oBsph51lPzVnr7t2b200=
|
||||
github.com/moby/buildkit v0.13.0-beta1.0.20231219135447-957cb50df991/go.mod h1:6MddWPSL5jxy+W8eMMHWDOfZzzRRKWXPZqajw72YHBc=
|
||||
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
|
||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||
github.com/moby/sys/mountinfo v0.7.1 h1:/tTvQaSJRr2FshkhXiIpux6fQ2Zvc4j7tAhMTStAG2g=
|
||||
github.com/moby/sys/mountinfo v0.7.1/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
|
||||
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||
github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI=
|
||||
github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg=
|
||||
github.com/moby/sys/symlink v0.2.0 h1:tk1rOM+Ljp0nFmfOIBtlV3rTDlWOwFRhjEeAhZB0nZc=
|
||||
github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs=
|
||||
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
|
||||
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/oschwald/maxminddb-golang v1.7.0 h1:JmU4Q1WBv5Q+2KZy5xJI+98aUwTIrPPxZUkd5Cwr8Zc=
|
||||
github.com/oschwald/maxminddb-golang v1.7.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis=
|
||||
github.com/paulmach/orb v0.7.1 h1:Zha++Z5OX/l168sqHK3k4z18LDvr+YAO/VjK0ReQ9rU=
|
||||
github.com/paulmach/orb v0.7.1/go.mod h1:FWRlTgl88VI1RBx/MkrwWDRhQ96ctqMCh8boXhmqB/A=
|
||||
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
|
||||
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
|
||||
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
|
||||
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
||||
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
|
||||
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
|
||||
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
|
||||
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
|
|
@ -479,22 +194,8 @@ github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
|||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs=
|
||||
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 h1:ka9QPuQg2u4LGipiZGsgkg3rJCo4iIUCy75FddM0GRQ=
|
||||
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc=
|
||||
github.com/sethvargo/go-envconfig v0.7.0 h1:P/ljQXSRjgAgsnIripHs53Jg/uNVXu2FYQ9yLSDappA=
|
||||
github.com/sethvargo/go-envconfig v0.7.0/go.mod h1:00S1FAhRUuTNJazWBWcJGvEHOM+NO6DhoRMAOX7FY5o=
|
||||
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
|
||||
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
|
||||
github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
|
||||
github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||
|
|
@ -503,108 +204,37 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
|
|||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/testcontainers/testcontainers-go v0.29.1 h1:z8kxdFlovA2y97RWx98v/TQ+tR+SXZm6p35M+xB92zk=
|
||||
github.com/testcontainers/testcontainers-go v0.29.1/go.mod h1:SnKnKQav8UcgtKqjp/AD8bE1MqZm+3TDb/B8crE3XnI=
|
||||
github.com/testcontainers/testcontainers-go/modules/compose v0.29.1 h1:47ipPM+s+ltCDOP3Sa1j95AkNb+z+WGiHLDbLU8ixuc=
|
||||
github.com/testcontainers/testcontainers-go/modules/compose v0.29.1/go.mod h1:Sqh+Ef2ESdbJQjTJl57UOkEHkOc7gXvQLg1b5xh6f1Y=
|
||||
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
|
||||
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
|
||||
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 h1:QB54BJwA6x8QU9nHY3xJSZR2kX9bgpZekRKGkLTmEXA=
|
||||
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375/go.mod h1:xRroudyp5iVtxKqZCrA6n2TLFRBf8bmnjr1UD4x+z7g=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc=
|
||||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20230825212630-f09800878302 h1:ZT8ibgassurSISJ1Pj26NsM3vY2jxFZn63Nd/TpHmRw=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20230825212630-f09800878302/go.mod h1:9kMVqMyQ/Sx2df5LtnGG+nbrmiZzCS7V6gjW3oGHsvI=
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
|
||||
github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531 h1:Y/M5lygoNPKwVNLMPXgVfsRT40CSFKXCxuU8LoHySjs=
|
||||
github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc=
|
||||
github.com/ua-parser/uap-go v0.0.0-20200325213135-e1c09f13e2fe h1:aj/vX5epIlQQBEocKoM9nSAiNpakdQzElc8SaRFPu+I=
|
||||
github.com/ua-parser/uap-go v0.0.0-20200325213135-e1c09f13e2fe/go.mod h1:OBcG9bn7sHtXgarhUEb3OfCnNsgtGnkVf41ilSZ3K3E=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.45.0 h1:2ea0IkZBsWH+HA2GkD+7+hRw2u97jzdFyRtXuO14a1s=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.45.0/go.mod h1:4m3RnBBb+7dB9d21y510oO1pdB1V4J6smNf14WXcBFQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
||||
go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
|
||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 h1:ZtfnDL+tUrs1F0Pzfwbg2d59Gru9NCH3bgSHBM6LDwU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0/go.mod h1:hG4Fj/y8TR/tlEDREo8tWstl9fO9gcFkn4xrx0Io8xU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 h1:NmnYCiR0qNufkldjVvyQfZTHSdzeHoZ41zggMsdMcLM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0/go.mod h1:UVAO61+umUsHLtYb8KXXRoHtxUkdOPkYidzW3gipRLQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0 h1:wNMDy/LVGLj2h3p6zg4d0gypKfWKSWI14E1C4smOgl8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0/go.mod h1:YfbDdXAAkemWJK3H/DshvlrxqFB2rtW4rY6ky/3x/H0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.42.0 h1:jwV9iQdvp38fxXi8ZC+lNpxjK16MRcZlpDYvbuO1FiA=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.42.0/go.mod h1:f3bYiqNqhoPxkvI2LrXqQVC546K7BuRDL/kKuxkujhA=
|
||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw=
|
||||
go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.19.0 h1:EJoTO5qysMsYCa+w4UghwFV/ptQgqSL/8Ni+hx+8i1k=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.19.0/go.mod h1:XjG0jQyFJrv2PbMvwND7LwCEhsJzCzV5210euduKcKY=
|
||||
go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
|
|
@ -628,51 +258,28 @@ golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWP
|
|||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw=
|
||||
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
||||
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
|
||||
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
|
@ -681,28 +288,21 @@ golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191220220014-0732a990476f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
|
@ -710,67 +310,28 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.169.0 h1:QwWPy71FgMWqJN/l6jVlFHUa29a7dcUy02I8o799nPY=
|
||||
google.golang.org/api v0.169.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20240325203815-454cdb8f5daa h1:ePqxpG3LVx+feAUOx8YmR5T7rc0rdzK8DyxM8cQ9zq0=
|
||||
google.golang.org/genproto v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:CnZenrTdRJb7jc+jOm0Rkywq+9wh0QC4U8tyiRbEPPM=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
@ -781,8 +342,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
@ -794,28 +353,4 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
k8s.io/api v0.26.7 h1:Lf4iEBEJb5OFNmawtBfSZV/UNi9riSJ0t1qdhyZqI40=
|
||||
k8s.io/api v0.26.7/go.mod h1:Vk9bMadzA49UHPmHB//lX7VRCQSXGoVwfLd3Sc1SSXI=
|
||||
k8s.io/apimachinery v0.26.7 h1:590jSBwaSHCAFCqltaEogY/zybFlhGsnLteLpuF2wig=
|
||||
k8s.io/apimachinery v0.26.7/go.mod h1:qYzLkrQ9lhrZRh0jNKo2cfvf/R1/kQONnSiyB7NUJU0=
|
||||
k8s.io/apiserver v0.26.7 h1:NX/zBZZn4R+Cq6shwyn8Pn8REd0yJJ16dbtv9WkEVEU=
|
||||
k8s.io/apiserver v0.26.7/go.mod h1:r0wDRWHI7VL/KlQLTkJJBVGZ3KeNfv+VetlyRtr86xs=
|
||||
k8s.io/client-go v0.26.7 h1:hyU9aKHlwVOykgyxzGYkrDSLCc4+mimZVyUJjPyUn1E=
|
||||
k8s.io/client-go v0.26.7/go.mod h1:okYjy0jtq6sdeztALDvCh24tg4opOQS1XNvsJlERDAo=
|
||||
k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw=
|
||||
k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E=
|
||||
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4=
|
||||
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrDp+YEkVDAHu7Jwk=
|
||||
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
tags.cncf.io/container-device-interface v0.6.2 h1:dThE6dtp/93ZDGhqaED2Pu374SOeUkBfuvkLuiTdwzg=
|
||||
tags.cncf.io/container-device-interface v0.6.2/go.mod h1:Shusyhjs1A5Na/kqPVLL0KqnHQHuunol9LFeUNkuGVE=
|
||||
|
|
|
|||
|
|
@ -182,3 +182,20 @@ def delete(tenant_id, user_id, role_id):
|
|||
{"tenant_id": tenant_id, "role_id": role_id})
|
||||
cur.execute(query=query)
|
||||
return get_roles(tenant_id=tenant_id)
|
||||
|
||||
|
||||
def get_role(tenant_id, role_id):
|
||||
with pg_client.PostgresClient() as cur:
|
||||
query = cur.mogrify("""SELECT roles.*
|
||||
FROM public.roles
|
||||
WHERE tenant_id =%(tenant_id)s
|
||||
AND deleted_at IS NULL
|
||||
AND not service_role
|
||||
AND role_id = %(role_id)s
|
||||
LIMIT 1;""",
|
||||
{"tenant_id": tenant_id, "role_id": role_id})
|
||||
cur.execute(query=query)
|
||||
row = cur.fetchone()
|
||||
if row is not None:
|
||||
row["created_at"] = TimeUTC.datetime_to_timestamp(row["created_at"])
|
||||
return helper.dict_to_camel_case(row)
|
||||
|
|
|
|||
|
|
@ -199,6 +199,12 @@ def create_member(tenant_id, user_id, data: schemas.CreateMemberSchema, backgrou
|
|||
role_id = data.roleId
|
||||
if role_id is None:
|
||||
role_id = roles.get_role_by_name(tenant_id=tenant_id, name="member").get("roleId")
|
||||
else:
|
||||
role = roles.get_role(tenant_id=tenant_id, role_id=role_id)
|
||||
if role is None:
|
||||
return {"errors": ["role not found"]}
|
||||
if role["name"].lower() == "owner" and role["protected"]:
|
||||
return {"errors": ["invalid role"]}
|
||||
invitation_token = __generate_invitation_token()
|
||||
user = get_deleted_user_by_email(email=data.email)
|
||||
if user is not None and user["tenantId"] == tenant_id:
|
||||
|
|
@ -333,7 +339,7 @@ def edit_member(user_id_to_update, tenant_id, changes: schemas.EditMemberSchema,
|
|||
if editor_id != user_id_to_update:
|
||||
admin = get_user_role(tenant_id=tenant_id, user_id=editor_id)
|
||||
if not admin["superAdmin"] and not admin["admin"]:
|
||||
return {"errors": ["unauthorized"]}
|
||||
return {"errors": ["unauthorized, you must have admin privileges"]}
|
||||
if admin["admin"] and user["superAdmin"]:
|
||||
return {"errors": ["only the owner can edit his own details"]}
|
||||
else:
|
||||
|
|
@ -343,10 +349,10 @@ def edit_member(user_id_to_update, tenant_id, changes: schemas.EditMemberSchema,
|
|||
return {"errors": ["cannot change your own admin privileges"]}
|
||||
if changes.roleId:
|
||||
if user["superAdmin"] and changes.roleId != user["roleId"]:
|
||||
changes.roleId = None
|
||||
return {"errors": ["owner's role cannot be changed"]}
|
||||
|
||||
if changes.roleId != user["roleId"]:
|
||||
elif user["superAdmin"]:
|
||||
changes.roleId = None
|
||||
elif changes.roleId != user["roleId"]:
|
||||
return {"errors": ["cannot change your own role"]}
|
||||
|
||||
if changes.name and len(changes.name) > 0:
|
||||
|
|
@ -357,6 +363,12 @@ def edit_member(user_id_to_update, tenant_id, changes: schemas.EditMemberSchema,
|
|||
|
||||
if changes.roleId is not None:
|
||||
_changes["roleId"] = changes.roleId
|
||||
role = roles.get_role(tenant_id=tenant_id, role_id=changes.roleId)
|
||||
if role is None:
|
||||
return {"errors": ["role not found"]}
|
||||
else:
|
||||
if role["name"].lower() == "owner" and role["protected"]:
|
||||
return {"errors": ["invalid role"]}
|
||||
|
||||
if len(_changes.keys()) > 0:
|
||||
update(tenant_id=tenant_id, user_id=user_id_to_update, changes=_changes, output=False)
|
||||
|
|
@ -540,12 +552,6 @@ def set_password_invitation(tenant_id, user_id, new_password):
|
|||
user = update(tenant_id=tenant_id, user_id=user_id, changes=changes)
|
||||
r = authenticate(user['email'], new_password)
|
||||
|
||||
tenant_id = r.pop("tenantId")
|
||||
r["limits"] = {
|
||||
"teamMember": -1,
|
||||
"projects": -1,
|
||||
"metadata": metadata.get_remaining_metadata_with_count(tenant_id)}
|
||||
|
||||
return {
|
||||
"jwt": r.pop("jwt"),
|
||||
"refreshToken": r.pop("refreshToken"),
|
||||
|
|
@ -554,10 +560,7 @@ def set_password_invitation(tenant_id, user_id, new_password):
|
|||
"spotRefreshToken": r.pop("spotRefreshToken"),
|
||||
"spotRefreshTokenMaxAge": r.pop("spotRefreshTokenMaxAge"),
|
||||
"tenantId": tenant_id,
|
||||
'data': {
|
||||
"scopeState": scope.get_scope(tenant_id),
|
||||
"user": r
|
||||
}
|
||||
**r
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -136,13 +136,13 @@ def add_edit(tenant_id, data: schemas.WebhookSchema, replace_none=None):
|
|||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"name already exists.")
|
||||
if data.webhook_id is not None:
|
||||
return update(tenant_id=tenant_id, webhook_id=data.webhook_id,
|
||||
changes={"endpoint": data.endpoint.unicode_string(),
|
||||
changes={"endpoint": data.endpoint,
|
||||
"authHeader": data.auth_header,
|
||||
"name": data.name},
|
||||
replace_none=replace_none)
|
||||
else:
|
||||
return add(tenant_id=tenant_id,
|
||||
endpoint=data.endpoint.unicode_string(),
|
||||
endpoint=data.endpoint,
|
||||
auth_header=data.auth_header,
|
||||
name=data.name,
|
||||
replace_none=replace_none)
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ def get_event_type(event_type: Union[schemas.EventType, schemas.PerformanceEvent
|
|||
schemas.EventType.REQUEST: "REQUEST",
|
||||
schemas.EventType.REQUEST_DETAILS: "REQUEST",
|
||||
schemas.PerformanceEventType.FETCH_FAILED: "REQUEST",
|
||||
schemas.GraphqlFilterType.GRAPHQL_NAME: "GRAPHQL",
|
||||
schemas.EventType.STATE_ACTION: "STATEACTION",
|
||||
schemas.EventType.ERROR: "ERROR",
|
||||
schemas.PerformanceEventType.LOCATION_AVG_CPU_LOAD: 'PERFORMANCE',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#!/bin/sh
|
||||
export TZ=UTC
|
||||
sh env_vars.sh
|
||||
source /tmp/.env.override
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#!/bin/sh
|
||||
export TZ=UTC
|
||||
export ASSIST_KEY=ignore
|
||||
sh env_vars.sh
|
||||
source /tmp/.env.override
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#!/bin/sh
|
||||
export TZ=UTC
|
||||
export ASSIST_KEY=ignore
|
||||
sh env_vars.sh
|
||||
source /tmp/.env.override
|
||||
|
|
|
|||
|
|
@ -198,6 +198,11 @@ export default class APIClient {
|
|||
}
|
||||
|
||||
return fetch(edp + _path, init).then((response) => {
|
||||
if (response.status === 403) {
|
||||
console.warn('API returned 403. Clearing JWT token.');
|
||||
this.onUpdateJwt({ jwt: undefined }); // Clear the token
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
return response;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ function UserForm() {
|
|||
const isSaving = userStore.saving;
|
||||
const user: any = userStore.instance || userStore.initUser();
|
||||
const roles = roleStore.list
|
||||
.filter((r) => (r.isProtected ? user.isSuperAdmin : true))
|
||||
.filter((r) => (r.protected ? user.isSuperAdmin : true))
|
||||
.map((r) => ({ label: r.name, value: r.roleId }));
|
||||
|
||||
const onChangeCheckbox = (e: any) => {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import {
|
|||
import React from 'react';
|
||||
|
||||
import ExCard from './ExCard';
|
||||
import { size } from '@floating-ui/react-dom-interactions';
|
||||
|
||||
const TYPES = {
|
||||
Frustrations: 'frustrations',
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ function ExcludeFilters(props: Props) {
|
|||
onRemoveFilter={() => onRemoveFilter(index)}
|
||||
// saveRequestPayloads={saveRequestPayloads}
|
||||
disableDelete={false}
|
||||
allowedFilterKeys={[FilterKey.LOCATION, FilterKey.CLICK, FilterKey.INPUT, FilterKey.CUSTOM]}
|
||||
// excludeFilterKeys={excludeFilterKeys}
|
||||
/>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -136,9 +136,10 @@ function FilterSeries(props: Props) {
|
|||
toggleExpand={() => setExpanded(!expanded)}/>
|
||||
)}
|
||||
|
||||
{expandable && (
|
||||
{expandable && !expanded && (
|
||||
<Space className="justify-between w-full px-5 py-2 cursor-pointer" onClick={() => setExpanded(!expanded)}>
|
||||
<div>{!expanded && <FilterCountLabels filters={series.filter.filters} toggleExpand={() => setExpanded(!expanded)}/>}</div>
|
||||
<FilterCountLabels filters={series.filter.filters} toggleExpand={() => setExpanded(!expanded)}/>
|
||||
{/*<div>{!expanded && <FilterCountLabels filters={series.filter.filters} toggleExpand={() => setExpanded(!expanded)}/>}</div>*/}
|
||||
<Button size="small"
|
||||
icon={expanded ? <ChevronUp size={16}/> : <ChevronDown size={16}/>}/>
|
||||
</Space>
|
||||
|
|
@ -156,13 +157,13 @@ function FilterSeries(props: Props) {
|
|||
supportsEmpty={supportsEmpty}
|
||||
onFilterMove={onFilterMove}
|
||||
excludeFilterKeys={excludeFilterKeys}
|
||||
// actions={[
|
||||
// expandable && (
|
||||
// <Button onClick={() => setExpanded(!expanded)}
|
||||
// size="small"
|
||||
// icon={expanded ? <ChevronUp size={16}/> : <ChevronDown size={16}/>}/>
|
||||
// )
|
||||
// ]}
|
||||
actions={[
|
||||
expandable && (
|
||||
<Button onClick={() => setExpanded(!expanded)}
|
||||
size="small"
|
||||
icon={expanded ? <ChevronUp size={16}/> : <ChevronDown size={16}/>}/>
|
||||
)
|
||||
]}
|
||||
/>
|
||||
) : (
|
||||
<div className="color-gray-medium">{emptyMessage}</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { useStore } from 'App/mstore';
|
||||
import React from 'react';
|
||||
// import Select from 'Shared/Select';
|
||||
import { Select } from 'antd';
|
||||
|
||||
const sortOptions = [
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ const FilterSection = observer(({ metric, excludeFilterKeys }: any) => {
|
|||
const eventsLength = metric.series[0].filter.filters.filter((i: any) => i && i.isEvent).length;
|
||||
// const cannotSaveFunnel = isFunnel && (!metric.series[0] || eventsLength <= 1);
|
||||
|
||||
const isSingleSeries = isTable || isFunnel || isClickMap || isInsights || isRetention;
|
||||
const isSingleSeries = isTable || isFunnel || isClickMap || isInsights || isRetention || isPathAnalysis;
|
||||
|
||||
// const onAddFilter = (filter: any) => {
|
||||
// metric.series[0].filter.addFilter(filter);
|
||||
|
|
|
|||
|
|
@ -1,163 +1,187 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import Widget from 'App/mstore/types/widget';
|
||||
import Funnelbar, { UxTFunnelBar } from "./FunnelBar";
|
||||
import Funnelbar, { UxTFunnelBar } from './FunnelBar';
|
||||
import cn from 'classnames';
|
||||
import stl from './FunnelWidget.module.css';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { NoContent, Icon } from 'UI';
|
||||
import { Tag, Tooltip } from 'antd';
|
||||
import { useModal } from 'App/components/Modal';
|
||||
import { useStore } from '@/mstore';
|
||||
import Filter from '@/mstore/types/filter';
|
||||
|
||||
interface Props {
|
||||
metric?: Widget;
|
||||
isWidget?: boolean;
|
||||
data: any;
|
||||
metric?: Widget;
|
||||
isWidget?: boolean;
|
||||
data: any;
|
||||
}
|
||||
|
||||
function FunnelWidget(props: Props) {
|
||||
const [focusedFilter, setFocusedFilter] = React.useState<number | null>(null);
|
||||
const { isWidget = false, data, metric } = props;
|
||||
const funnel = data.funnel || { stages: [] };
|
||||
const totalSteps = funnel.stages.length;
|
||||
const stages = isWidget ? [...funnel.stages.slice(0, 1), funnel.stages[funnel.stages.length - 1]] : funnel.stages;
|
||||
const hasMoreSteps = funnel.stages.length > 2;
|
||||
const lastStage = funnel.stages[funnel.stages.length - 1];
|
||||
const remainingSteps = totalSteps - 2;
|
||||
const { hideModal } = useModal();
|
||||
const metricLabel = metric?.metricFormat == 'userCount' ? 'Users' : 'Sessions';
|
||||
const { dashboardStore, searchStore } = useStore();
|
||||
const [focusedFilter, setFocusedFilter] = React.useState<number | null>(null);
|
||||
const { isWidget = false, data, metric } = props;
|
||||
const funnel = data.funnel || { stages: [] };
|
||||
const totalSteps = funnel.stages.length;
|
||||
const stages = isWidget ? [...funnel.stages.slice(0, 1), funnel.stages[funnel.stages.length - 1]] : funnel.stages;
|
||||
const hasMoreSteps = funnel.stages.length > 2;
|
||||
const lastStage = funnel.stages[funnel.stages.length - 1];
|
||||
const remainingSteps = totalSteps - 2;
|
||||
const { hideModal } = useModal();
|
||||
const metricLabel = metric?.metricFormat == 'userCount' ? 'Users' : 'Sessions';
|
||||
const drillDownFilter = dashboardStore.drillDownFilter;
|
||||
const drillDownPeriod = dashboardStore.drillDownPeriod;
|
||||
const metricFilters = metric?.series[0]?.filter.filters || [];
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (isWidget) return;
|
||||
hideModal();
|
||||
const applyDrillDown = (index: number) => {
|
||||
const filter = new Filter().fromData({ filters: metricFilters.slice(0, index + 1) });
|
||||
const periodTimestamps = drillDownPeriod.toTimestamps();
|
||||
drillDownFilter.merge({
|
||||
filters: filter.toJson().filters,
|
||||
startTimestamp: periodTimestamps.startTimestamp,
|
||||
endTimestamp: periodTimestamps.endTimestamp
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (isWidget) return;
|
||||
hideModal();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const focusStage = (index: number) => {
|
||||
funnel.stages.forEach((s, i) => {
|
||||
// turning on all filters if one was focused already
|
||||
if (focusedFilter === index) {
|
||||
s.updateKey('isActive', true);
|
||||
setFocusedFilter(null);
|
||||
} else {
|
||||
setFocusedFilter(index);
|
||||
if (i === index) {
|
||||
s.updateKey('isActive', true);
|
||||
} else {
|
||||
s.updateKey('isActive', false);
|
||||
}
|
||||
}, []);
|
||||
}
|
||||
});
|
||||
|
||||
const focusStage = (index: number) => {
|
||||
funnel.stages.forEach((s, i) => {
|
||||
// turning on all filters if one was focused already
|
||||
if (focusedFilter === index) {
|
||||
s.updateKey('isActive', true)
|
||||
setFocusedFilter(null)
|
||||
} else {
|
||||
setFocusedFilter(index)
|
||||
if (i === index) {
|
||||
s.updateKey('isActive', true)
|
||||
} else {
|
||||
s.updateKey('isActive', false)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
applyDrillDown(focusedFilter === index ? -1 : index);
|
||||
};
|
||||
|
||||
return (
|
||||
<NoContent
|
||||
style={{ minHeight: 220 }}
|
||||
title={
|
||||
<div className="flex items-center text-lg">
|
||||
<Icon name="info-circle" className="mr-2" size="18" />
|
||||
No data available for the selected period.
|
||||
</div>
|
||||
}
|
||||
show={!stages || stages.length === 0}
|
||||
>
|
||||
<div className="w-full">
|
||||
{ !isWidget && (
|
||||
stages.map((filter: any, index: any) => (
|
||||
<Stage
|
||||
key={index}
|
||||
index={index + 1}
|
||||
isWidget={isWidget}
|
||||
stage={filter}
|
||||
focusStage={focusStage}
|
||||
focusedFilter={focusedFilter}
|
||||
metricLabel={metricLabel}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
return (
|
||||
<NoContent
|
||||
style={{ minHeight: 220 }}
|
||||
title={
|
||||
<div className="flex items-center text-lg">
|
||||
<Icon name="info-circle" className="mr-2" size="18" />
|
||||
No data available for the selected period.
|
||||
</div>
|
||||
}
|
||||
show={!stages || stages.length === 0}
|
||||
>
|
||||
<div className="w-full">
|
||||
{!isWidget && (
|
||||
stages.map((filter: any, index: any) => (
|
||||
<Stage
|
||||
key={index}
|
||||
index={index + 1}
|
||||
isWidget={isWidget}
|
||||
stage={filter}
|
||||
focusStage={focusStage}
|
||||
focusedFilter={focusedFilter}
|
||||
metricLabel={metricLabel}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
|
||||
{ isWidget && (
|
||||
<>
|
||||
<Stage index={1} isWidget={isWidget} stage={stages[0]} />
|
||||
{isWidget && (
|
||||
<>
|
||||
<Stage index={1} isWidget={isWidget} stage={stages[0]} />
|
||||
|
||||
{ hasMoreSteps && (
|
||||
<>
|
||||
<EmptyStage total={remainingSteps} />
|
||||
</>
|
||||
)}
|
||||
{hasMoreSteps && (
|
||||
<>
|
||||
<EmptyStage total={remainingSteps} />
|
||||
</>
|
||||
)}
|
||||
|
||||
{funnel.stages.length > 1 && (
|
||||
<Stage index={totalSteps} isWidget={isWidget} stage={lastStage} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center pb-4">
|
||||
<div className="flex items-center">
|
||||
<span className="text-base font-medium mr-2">Lost conversion</span>
|
||||
<Tooltip title={`${funnel.lostConversions} Sessions ${funnel.lostConversionsPercentage}%`}>
|
||||
<Tag bordered={false} color="red" className='text-lg font-medium rounded-lg'>
|
||||
{funnel.lostConversions}
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="mx-3" />
|
||||
<div className="flex items-center">
|
||||
<span className="text-base font-medium mr-2">Total conversion</span>
|
||||
<Tooltip title={`${funnel.totalConversions} Sessions ${funnel.totalConversionsPercentage}%`}>
|
||||
<Tag bordered={false} color="green" className='text-lg font-medium rounded-lg'>
|
||||
{funnel.totalConversions}
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
{funnel.totalDropDueToIssues > 0 && <div className="flex items-center mb-2"><Icon name="magic" /> <span className="ml-2">{funnel.totalDropDueToIssues} sessions dropped due to issues.</span></div>}
|
||||
</NoContent>
|
||||
);
|
||||
{funnel.stages.length > 1 && (
|
||||
<Stage index={totalSteps} isWidget={isWidget} stage={lastStage} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center pb-4">
|
||||
<div className="flex items-center">
|
||||
<span className="text-base font-medium mr-2">Lost conversion</span>
|
||||
<Tooltip title={`${funnel.lostConversions} Sessions ${funnel.lostConversionsPercentage}%`}>
|
||||
<Tag bordered={false} color="red" className="text-lg font-medium rounded-lg">
|
||||
{funnel.lostConversions}
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="mx-3" />
|
||||
<div className="flex items-center">
|
||||
<span className="text-base font-medium mr-2">Total conversion</span>
|
||||
<Tooltip title={`${funnel.totalConversions} Sessions ${funnel.totalConversionsPercentage}%`}>
|
||||
<Tag bordered={false} color="green" className="text-lg font-medium rounded-lg">
|
||||
{funnel.totalConversions}
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
{funnel.totalDropDueToIssues > 0 && <div className="flex items-center mb-2"><Icon name="magic" /> <span
|
||||
className="ml-2">{funnel.totalDropDueToIssues} sessions dropped due to issues.</span></div>}
|
||||
</NoContent>
|
||||
);
|
||||
}
|
||||
|
||||
export const EmptyStage = observer(({ total }: any) => {
|
||||
return (
|
||||
<div className={cn("flex items-center mb-4 pb-3", stl.step)}>
|
||||
<IndexNumber index={0} />
|
||||
<div className="w-fit px-2 border border-teal py-1 text-center justify-center bg-teal-lightest flex items-center rounded-full color-teal" style={{ width: '100px'}}>
|
||||
{`+${total} ${total > 1 ? 'steps' : 'step'}`}
|
||||
</div>
|
||||
<div className="border-b w-full border-dashed"></div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
return (
|
||||
<div className={cn('flex items-center mb-4 pb-3', stl.step)}>
|
||||
<IndexNumber index={0} />
|
||||
<div
|
||||
className="w-fit px-2 border border-teal py-1 text-center justify-center bg-teal-lightest flex items-center rounded-full color-teal"
|
||||
style={{ width: '100px' }}>
|
||||
{`+${total} ${total > 1 ? 'steps' : 'step'}`}
|
||||
</div>
|
||||
<div className="border-b w-full border-dashed"></div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const Stage = observer(({ metricLabel, stage, index, isWidget, uxt, focusStage, focusedFilter }: any) => {
|
||||
return stage ? (
|
||||
<div
|
||||
className={cn('flex items-start', stl.step, { [stl['step-disabled']]: !stage.isActive })}
|
||||
>
|
||||
<IndexNumber index={index} />
|
||||
{!uxt ? <Funnelbar metricLabel={metricLabel} index={index} filter={stage} focusStage={focusStage} focusedFilter={focusedFilter} /> : <UxTFunnelBar filter={stage} />}
|
||||
{/*{!isWidget && !uxt && <BarActions bar={stage} />}*/}
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)
|
||||
})
|
||||
return stage ? (
|
||||
<div
|
||||
className={cn('flex items-start', stl.step, { [stl['step-disabled']]: !stage.isActive })}
|
||||
>
|
||||
<IndexNumber index={index} />
|
||||
{!uxt ? <Funnelbar metricLabel={metricLabel} index={index} filter={stage} focusStage={focusStage}
|
||||
focusedFilter={focusedFilter} /> : <UxTFunnelBar filter={stage} />}
|
||||
{/*{!isWidget && !uxt && <BarActions bar={stage} />}*/}
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
});
|
||||
|
||||
export const IndexNumber = observer(({ index }: any) => {
|
||||
return (
|
||||
<div className="z-10 w-6 h-6 border shrink-0 mr-4 text-sm rounded-full bg-gray-lightest flex items-center justify-center leading-3">
|
||||
{index === 0 ? <Icon size="14" color="gray-dark" name="list" /> : index}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
return (
|
||||
<div
|
||||
className="z-10 w-6 h-6 border shrink-0 mr-4 text-sm rounded-full bg-gray-lightest flex items-center justify-center leading-3">
|
||||
{index === 0 ? <Icon size="14" color="gray-dark" name="list" /> : index}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
const BarActions = observer(({ bar }: any) => {
|
||||
return (
|
||||
<div className="self-end flex items-center justify-center ml-4" style={{ marginBottom: '49px'}}>
|
||||
<button onClick={() => bar.updateKey('isActive', !bar.isActive)}>
|
||||
<Icon name="eye-slash-fill" color={bar.isActive ? "gray-light" : "gray-darkest"} size="22" />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
return (
|
||||
<div className="self-end flex items-center justify-center ml-4" style={{ marginBottom: '49px' }}>
|
||||
<button onClick={() => bar.updateKey('isActive', !bar.isActive)}>
|
||||
<Icon name="eye-slash-fill" color={bar.isActive ? 'gray-light' : 'gray-darkest'} size="22" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default observer(FunnelWidget);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import { withRouter, RouteComponentProps, useLocation } from 'react-router-dom';
|
|||
import FlagView from 'Components/FFlags/FlagView/FlagView';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useStore } from '@/mstore';
|
||||
import NotesList from 'Shared/SessionsTabOverview/components/Notes/NoteList';
|
||||
import NoteTags from 'Shared/SessionsTabOverview/components/Notes/NoteTags';
|
||||
|
||||
// @ts-ignore
|
||||
interface IProps extends RouteComponentProps {
|
||||
|
|
@ -36,15 +38,16 @@ function Overview({ match: { params } }: IProps) {
|
|||
return (
|
||||
<Switch>
|
||||
<Route exact strict
|
||||
path={[withSiteId(sessions(), siteId), withSiteId(notes(), siteId), withSiteId(bookmarks(), siteId)]}>
|
||||
path={[withSiteId(sessions(), siteId), withSiteId(bookmarks(), siteId)]}>
|
||||
<div className="mb-5 w-full mx-auto" style={{ maxWidth: '1360px' }}>
|
||||
<NoSessionsMessage siteId={siteId} />
|
||||
<MainSearchBar />
|
||||
<SessionSearch />
|
||||
<div className="my-4" />
|
||||
<SessionsTabOverview />
|
||||
</div>
|
||||
</Route>
|
||||
<Route exact strict path={withSiteId(notes(), siteId)}>
|
||||
<div className="mb-5 w-full mx-auto" style={{ maxWidth: '1360px' }}>
|
||||
<NotesList />
|
||||
</div>
|
||||
</Route>
|
||||
<Route exact strict path={withSiteId(fflags(), siteId)}>
|
||||
<FFlagsList siteId={siteId} />
|
||||
</Route>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import {
|
|||
import { useStore } from 'App/mstore';
|
||||
import { session as sessionRoute, withSiteId } from 'App/routes';
|
||||
import { SummaryButton } from 'Components/Session_/Player/Controls/Controls';
|
||||
import { MobEventsList, WebEventsList } from "../../../Session_/Player/Controls/EventsList";
|
||||
import useShortcuts from '../ReplayPlayer/useShortcuts';
|
||||
|
||||
export const SKIP_INTERVALS = {
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ function PlayerBlockHeader(props: any) {
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative border-l border-l-gray-lighter" style={{ minWidth: '270px' }}>
|
||||
<div className="px-2 relative border-l border-l-gray-lighter" style={{ minWidth: '270px' }}>
|
||||
<Tabs
|
||||
tabs={TABS}
|
||||
active={activeTab}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
import { Segmented } from 'antd';
|
||||
import React from 'react';
|
||||
import { VList, VListHandle } from 'virtua';
|
||||
import { PlayerContext } from "App/components/Session/playerContext";
|
||||
import { PlayerContext } from 'App/components/Session/playerContext';
|
||||
import { processLog, UnifiedLog } from './utils';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useStore } from 'App/mstore';
|
||||
|
|
@ -12,13 +11,12 @@ import {
|
|||
} from 'App/components/Client/Integrations/apiMethods';
|
||||
import BottomBlock from 'App/components/shared/DevTools/BottomBlock';
|
||||
import { capitalize } from 'App/utils';
|
||||
import { Icon, Input } from 'UI';
|
||||
import { Icon } from 'UI';
|
||||
import { Segmented, Input, Tooltip } from 'antd';
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
import { client } from 'App/mstore';
|
||||
import { FailedFetch, LoadingFetch } from "./StatusMessages";
|
||||
import {
|
||||
TableHeader,
|
||||
LogRow
|
||||
} from './Table'
|
||||
import { FailedFetch, LoadingFetch } from './StatusMessages';
|
||||
import { TableHeader, LogRow } from './Table';
|
||||
|
||||
async function fetchLogs(
|
||||
tab: string,
|
||||
|
|
@ -30,23 +28,24 @@ async function fetchLogs(
|
|||
);
|
||||
const json = await data.json();
|
||||
try {
|
||||
const logsResp = await fetch(json.url)
|
||||
const logsResp = await fetch(json.url);
|
||||
if (logsResp.ok) {
|
||||
const logJson = await logsResp.json()
|
||||
if (logJson.length === 0) return []
|
||||
return processLog(logJson)
|
||||
const logJson = await logsResp.json();
|
||||
if (logJson.length === 0) return [];
|
||||
return processLog(logJson);
|
||||
} else {
|
||||
throw new Error('Failed to fetch logs')
|
||||
throw new Error('Failed to fetch logs');
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
throw e
|
||||
console.log(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function BackendLogsPanel() {
|
||||
const { projectsStore, sessionStore, integrationsStore } = useStore();
|
||||
const integratedServices = integrationsStore.integrations.backendLogIntegrations;
|
||||
const integratedServices =
|
||||
integrationsStore.integrations.backendLogIntegrations;
|
||||
const defaultTab = integratedServices[0]!.name;
|
||||
const sessionId = sessionStore.currentId;
|
||||
const projectId = projectsStore.siteId!;
|
||||
|
|
@ -82,40 +81,59 @@ function BackendLogsPanel() {
|
|||
return (
|
||||
<BottomBlock style={{ height: '100%' }}>
|
||||
<BottomBlock.Header>
|
||||
<div className={'flex gap-2 items-center w-full'}>
|
||||
<div className={'font-semibold'}>Traces</div>
|
||||
{tabs.length && tab ? (
|
||||
<div>
|
||||
<Segmented options={tabs} value={tab} onChange={setTab} />
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<div className={'flex gap-2 items-center'}>
|
||||
<div className={'font-semibold'}>Traces</div>
|
||||
{tabs.length && tab ? (
|
||||
<div>
|
||||
<Segmented
|
||||
options={tabs}
|
||||
value={tab}
|
||||
onChange={setTab}
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className={'ml-auto'} />
|
||||
<Input
|
||||
className="input-small h-8"
|
||||
placeholder="Filter by keyword"
|
||||
icon="search"
|
||||
name="filter"
|
||||
height={28}
|
||||
onChange={onFilterChange}
|
||||
value={filter}
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<Segmented
|
||||
options={[
|
||||
{ label: 'All Tabs', value: 'all' },
|
||||
{
|
||||
label: (
|
||||
<Tooltip title="Backend logs are fetched for all tabs combined.">
|
||||
<span>Current Tab</span>
|
||||
</Tooltip>
|
||||
),
|
||||
value: 'current',
|
||||
disabled: true,
|
||||
},
|
||||
]}
|
||||
defaultValue="all"
|
||||
size="small"
|
||||
className="rounded-full font-medium"
|
||||
/>
|
||||
|
||||
<Input
|
||||
className="rounded-lg"
|
||||
placeholder="Filter by keyword"
|
||||
name="filter"
|
||||
onChange={onFilterChange}
|
||||
value={filter}
|
||||
size="small"
|
||||
prefix={<SearchOutlined className="text-neutral-400" />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BottomBlock.Header>
|
||||
|
||||
<BottomBlock.Content className="overflow-y-auto">
|
||||
{isPending ? (
|
||||
<LoadingFetch provider={capitalize(tab)} />
|
||||
) : null}
|
||||
{isPending ? <LoadingFetch provider={capitalize(tab)} /> : null}
|
||||
{isError ? (
|
||||
<FailedFetch
|
||||
provider={capitalize(tab)}
|
||||
onRetry={refetch}
|
||||
/>
|
||||
) : null}
|
||||
{isSuccess ? (
|
||||
<LogsTable data={data} />
|
||||
<FailedFetch provider={capitalize(tab)} onRetry={refetch} />
|
||||
) : null}
|
||||
{isSuccess ? <LogsTable data={data} /> : null}
|
||||
</BottomBlock.Content>
|
||||
</BottomBlock>
|
||||
);
|
||||
|
|
@ -128,8 +146,10 @@ const LogsTable = observer(({ data }: { data: UnifiedLog[] }) => {
|
|||
const _list = React.useRef<VListHandle>(null);
|
||||
const activeIndex = React.useMemo(() => {
|
||||
const currTs = time + sessionStart;
|
||||
const index = data.findIndex(
|
||||
(log) => log.timestamp !== 'N/A' ? new Date(log.timestamp).getTime() >= currTs : false
|
||||
const index = data.findIndex((log) =>
|
||||
log.timestamp !== 'N/A'
|
||||
? new Date(log.timestamp).getTime() >= currTs
|
||||
: false
|
||||
);
|
||||
return index === -1 ? data.length - 1 : index;
|
||||
}, [time, data.length]);
|
||||
|
|
@ -141,17 +161,22 @@ const LogsTable = observer(({ data }: { data: UnifiedLog[] }) => {
|
|||
|
||||
const onJump = (ts: number) => {
|
||||
player.jump(ts - sessionStart);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<TableHeader size={data.length} />
|
||||
<VList ref={_list} count={data.length}>
|
||||
{data.map((log, index) => (
|
||||
<LogRow key={index} isActive={index === activeIndex} log={log} onJump={onJump} />
|
||||
<LogRow
|
||||
key={index}
|
||||
isActive={index === activeIndex}
|
||||
log={log}
|
||||
onJump={onJump}
|
||||
/>
|
||||
))}
|
||||
</VList>
|
||||
</>
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
export default observer(BackendLogsPanel);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export function LoadingFetch({ provider }: { provider: string }) {
|
|||
'w-full h-full flex items-center justify-center flex-col gap-2'
|
||||
}
|
||||
>
|
||||
<LoadingOutlined style={{ fontSize: 32 }} />
|
||||
<LoadingOutlined size={32} />
|
||||
<div>Fetching logs from {provider}...</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -33,16 +33,23 @@ export function FailedFetch({
|
|||
'w-full h-full flex flex-col items-center justify-center gap-2'
|
||||
}
|
||||
>
|
||||
<Icon name={'exclamation-circle'} size={32} />
|
||||
<div className={'flex items-center gap-1'}>
|
||||
|
||||
<div className={'flex items-center gap-1 font-medium'}>
|
||||
<Icon name={'exclamation-circle'} size={14} />
|
||||
<span>Failed to fetch logs from {provider}. </span>
|
||||
<div className={'link'} onClick={onRetry}>
|
||||
</div>
|
||||
|
||||
<div className='flex items-center gap-3'>
|
||||
|
||||
<Button type='text' size='small' onClick={onRetry}>
|
||||
Retry
|
||||
</div>
|
||||
</div>
|
||||
<div className={'link'} onClick={() => history.push(intPath)}>
|
||||
</Button>
|
||||
|
||||
<Button type='text' size='small' onClick={() => history.push(intPath)}>
|
||||
Check Configuration
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,51 @@ import { useStore } from 'App/mstore';
|
|||
import SaveModal from 'Components/Session/Player/TagWatch/SaveModal';
|
||||
import React from 'react';
|
||||
import { PlayerContext } from 'Components/Session/playerContext';
|
||||
import { Button, Input } from 'antd';
|
||||
import { CopyButton } from 'UI';
|
||||
import { SearchOutlined, ZoomInOutlined } from '@ant-design/icons';
|
||||
import { Button, Input, Tooltip } from 'antd';
|
||||
import { CopyOutlined } from '@ant-design/icons';
|
||||
import { ZoomInOutlined } from '@ant-design/icons';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useModal } from 'App/components/Modal';
|
||||
import { toast } from 'react-toastify';
|
||||
import { FilterKey } from "App/types/filter/filterType";
|
||||
import { addOptionsToFilter } from "App/types/filter/newFilter";
|
||||
import { FilterKey } from 'App/types/filter/filterType';
|
||||
import { addOptionsToFilter } from 'App/types/filter/newFilter';
|
||||
|
||||
interface CopyableTextAreaProps {
|
||||
selector: string;
|
||||
setSelector: (value: string) => void;
|
||||
}
|
||||
|
||||
const CopyableTextArea: React.FC<CopyableTextAreaProps> = ({ selector, setSelector }) => {
|
||||
const handleCopy = () => {
|
||||
navigator.clipboard.writeText(selector);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full relative">
|
||||
<Input.TextArea
|
||||
value={selector}
|
||||
onChange={(e) => setSelector(e.target.value)}
|
||||
className="rounded-lg font-mono text-sm placeholder:font-sans placeholder:text-base placeholder:text-gray-400"
|
||||
rows={4}
|
||||
style={{ paddingRight: '40px' }}
|
||||
placeholder='Enter selector to tag elements. E.g. .btn-primary'
|
||||
/>
|
||||
<Tooltip title="Copy">
|
||||
<Button
|
||||
type="text"
|
||||
icon={<CopyOutlined />}
|
||||
onClick={handleCopy}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '8px',
|
||||
right: '8px',
|
||||
zIndex: 1,
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function TagWatch() {
|
||||
const { tagWatchStore, searchStore } = useStore();
|
||||
|
|
@ -50,7 +87,7 @@ function TagWatch() {
|
|||
ignoreClickRage: ignoreClRage,
|
||||
ignoreDeadClick: ignoreDeadCl,
|
||||
});
|
||||
const tags = await tagWatchStore.getTags()
|
||||
const tags = await tagWatchStore.getTags();
|
||||
if (tags) {
|
||||
addOptionsToFilter(
|
||||
FilterKey.TAGGED_ELEMENT,
|
||||
|
|
@ -58,42 +95,41 @@ function TagWatch() {
|
|||
);
|
||||
searchStore.refreshFilterOptions();
|
||||
}
|
||||
// @ts-ignore
|
||||
toast.success('Tag created');
|
||||
setSelector('');
|
||||
return tag
|
||||
return tag;
|
||||
} catch {
|
||||
// @ts-ignore
|
||||
toast.error('Failed to create tag');
|
||||
}
|
||||
};
|
||||
|
||||
const openSaveModal = () => {
|
||||
if (selector === '') {
|
||||
return;
|
||||
}
|
||||
showModal(<SaveModal onSave={onSave} hideModal={hideModal} />, { right: true, width: 400 });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={'w-full h-full p-2 flex flex-col gap-2'}>
|
||||
<div className={'flex items-center justify-between'}>
|
||||
<div className={'font-semibold text-xl'}>Element Selector</div>
|
||||
<CopyButton content={selector} />
|
||||
<div className="w-full h-full p-4 flex flex-col gap-2">
|
||||
<div className="flex flex-col items-center justify-between">
|
||||
<p>Select elements in the session play area to tag by class selector and filter sessions to verify their rendering.</p>
|
||||
|
||||
</div>
|
||||
<Input.TextArea value={selector} onChange={(e) => setSelector(e.target.value)} />
|
||||
|
||||
<CopyableTextArea selector={selector} setSelector={setSelector} />
|
||||
|
||||
<Button
|
||||
onClick={openSaveModal}
|
||||
type={'primary'}
|
||||
type="primary"
|
||||
ghost
|
||||
icon={<ZoomInOutlined />}
|
||||
disabled={selector === ''}
|
||||
>
|
||||
Tag Element
|
||||
</Button>
|
||||
<div className={'text-disabled-text text-sm'}>
|
||||
Create and filter sessions by ‘watch elements’ to determine if they rendered or not.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(TagWatch);
|
||||
export default observer(TagWatch);
|
||||
|
|
@ -18,7 +18,7 @@ function RightBlock({
|
|||
switch (activeTab) {
|
||||
case 'EVENTS':
|
||||
return (
|
||||
<div className={cn('flex flex-col bg-white border-l', stl.panel)}>
|
||||
<div className={cn('flex flex-col border-l', stl.panel)}>
|
||||
<EventsBlock setActiveTab={setActiveTab} />
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ const Tabs = ({ tabs, active, onClick, border = true, className }: Props) => {
|
|||
return (
|
||||
<div className={cn(stl.tabs, className, { [stl.bordered]: border })} role="tablist">
|
||||
<Segmented
|
||||
size="small"
|
||||
value={active}
|
||||
options={tabs.map(({ key, text, hidden = false, disabled = false, iconComp = null }) => ({
|
||||
label: (
|
||||
|
|
@ -29,14 +30,14 @@ const Tabs = ({ tabs, active, onClick, border = true, className }: Props) => {
|
|||
onClick={() => {
|
||||
onClick(key);
|
||||
}}
|
||||
className={'font-semibold flex gap-1 items-center'}
|
||||
className={'font-medium flex gap-1 items-center hover:text-teal rounded-lg'}
|
||||
>
|
||||
{iconComp ? iconComp : <Icon size={16} color={'black'} name={iconMap[key as keyof typeof iconMap]} />}
|
||||
{iconComp ? iconComp : <Icon size={14} color="currentColor" style={{ fill: 'currentColor', strokeWidth:'0' }} name={iconMap[key as keyof typeof iconMap]} />}
|
||||
<span>{text}</span>
|
||||
</div>
|
||||
),
|
||||
value: key,
|
||||
disabled: disabled,
|
||||
disabled: disabled,
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ const Event: React.FC<Props> = ({
|
|||
>
|
||||
<div className={cn(cls.main, 'flex flex-col w-full')}>
|
||||
<div
|
||||
className={cn('flex items-center w-full', { 'px-4': isLocation })}
|
||||
className={cn('flex items-start w-full', { 'px-4': isLocation })}
|
||||
>
|
||||
<div style={{ minWidth: '16px' }}>
|
||||
{event.type && iconName ? (
|
||||
|
|
@ -169,20 +169,18 @@ const Event: React.FC<Props> = ({
|
|||
)}
|
||||
</div>
|
||||
<div className="ml-3 w-full">
|
||||
<div className="flex w-full items-first justify-between">
|
||||
<div className="flex w-full items-start">
|
||||
<div
|
||||
className="flex items-center w-full"
|
||||
className="flex flex-col justify-center items-start w-full"
|
||||
style={{ minWidth: '0' }}
|
||||
>
|
||||
<span
|
||||
className={cn(cls.title, { 'font-medium': isLocation })}
|
||||
>
|
||||
<span className={cn(cls.title, 'font-medium')}>
|
||||
{title}
|
||||
</span>
|
||||
{body && !isLocation && (
|
||||
<TextEllipsis
|
||||
maxWidth="60%"
|
||||
className="w-full ml-2 text-sm color-gray-medium"
|
||||
maxWidth="80%"
|
||||
className="w-full text-sm color-gray-medium"
|
||||
text={body}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -202,8 +200,7 @@ const Event: React.FC<Props> = ({
|
|||
{isLocation && (
|
||||
<div className="pt-1 px-4">
|
||||
<TextEllipsis
|
||||
maxWidth="80%"
|
||||
className="text-sm font-normal color-gray-medium"
|
||||
className="text-sm ms-8 font-normal color-gray-medium"
|
||||
text={body}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { Icon, TextEllipsis } from 'UI';
|
|||
import Event from './Event';
|
||||
import NoteEvent from './NoteEvent';
|
||||
import stl from './eventGroupWrapper.module.css';
|
||||
import cn from 'classnames'
|
||||
|
||||
function EventGroupWrapper(props) {
|
||||
const { userStore } = useStore();
|
||||
|
|
@ -132,7 +133,7 @@ function EventGroupWrapper(props) {
|
|||
{isFirst && isLocation && event.referrer && (
|
||||
<TextEllipsis>
|
||||
<div className={stl.referrer}>
|
||||
Referrer: <span className={stl.url}>{safeRef}</span>
|
||||
Referrer: <span className={cn(stl.url, '!font-normal')}>{safeRef}</span>
|
||||
</div>
|
||||
</TextEllipsis>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import React from 'react';
|
||||
import { Input, Button } from 'UI';
|
||||
import {Input, Button, Tooltip} from 'antd';
|
||||
import {CloseOutlined, SearchOutlined} from '@ant-design/icons';
|
||||
import { PlayerContext } from 'App/components/Session/playerContext';
|
||||
|
||||
function EventSearch(props) {
|
||||
const { player } = React.useContext(PlayerContext);
|
||||
|
||||
const { onChange, value, header, setActiveTab } = props;
|
||||
const { onChange, value, header, setActiveTab, eventsText } = props;
|
||||
|
||||
const toggleEvents = () => player.toggleEvents();
|
||||
|
||||
|
|
@ -16,25 +17,25 @@ function EventSearch(props) {
|
|||
<Input
|
||||
autoFocus
|
||||
type="text"
|
||||
placeholder="Filter"
|
||||
className="inset-0 w-full"
|
||||
placeholder={`Filter ${eventsText}`}
|
||||
className="w-full rounded-lg"
|
||||
name="query"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
wrapperClassName="w-full"
|
||||
style={{ height: '32px' }}
|
||||
autoComplete="off chromebugfix"
|
||||
prefix={<SearchOutlined />}
|
||||
/>
|
||||
|
||||
<Button
|
||||
className="ml-2"
|
||||
icon="close"
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
setActiveTab('');
|
||||
toggleEvents();
|
||||
}}
|
||||
/>
|
||||
<Tooltip title="Close Panel" placement='bottom' >
|
||||
<Button
|
||||
className="ml-2"
|
||||
type='text'
|
||||
onClick={() => {
|
||||
setActiveTab('');
|
||||
toggleEvents();
|
||||
}}
|
||||
icon={<CloseOutlined />}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ function EventsBlock(props: IProps) {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className={cn(styles.header, 'p-4')}>
|
||||
<div className={cn(styles.header, 'py-4 px-2 bg-gradient-to-t from-transparent to-neutral-50 h-[57px]' )}>
|
||||
{uxtestingStore.isUxt() ? (
|
||||
<div style={{ width: 240, height: 130 }} className={'relative'}>
|
||||
<video
|
||||
|
|
@ -219,14 +219,14 @@ function EventsBlock(props: IProps) {
|
|||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div className={cn(styles.hAndProgress, 'mt-3')}>
|
||||
<div className={cn(styles.hAndProgress, 'mt-0')}>
|
||||
<EventSearch
|
||||
onChange={write}
|
||||
setActiveTab={setActiveTab}
|
||||
value={query}
|
||||
eventsText={usedEvents.length ? `${usedEvents.length} Events` : '0 Events'}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-1 color-gray-medium">{eventsText}</div>
|
||||
</div>
|
||||
<div
|
||||
className={cn('flex-1 pb-4', styles.eventsList)}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { Segmented } from 'antd';
|
||||
import {InfoCircleOutlined} from '@ant-design/icons'
|
||||
import cn from 'classnames';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React, { useEffect } from 'react';
|
||||
|
|
@ -12,6 +13,7 @@ import SummaryBlock from 'Components/Session/Player/ReplayPlayer/SummaryBlock';
|
|||
import { SummaryButton } from 'Components/Session_/Player/Controls/Controls';
|
||||
import TimelineZoomButton from 'Components/Session_/Player/Controls/components/TimelineZoomButton';
|
||||
import { Icon, NoContent } from 'UI';
|
||||
import TabSelector from "../../shared/DevTools/TabSelector";
|
||||
|
||||
import BottomBlock from '../BottomBlock';
|
||||
import EventRow from './components/EventRow';
|
||||
|
|
@ -136,14 +138,60 @@ function WebOverviewPanelCont() {
|
|||
|
||||
const { endTime, currentTab, tabStates } = store.get();
|
||||
|
||||
const stackEventList = tabStates[currentTab]?.stackList || [];
|
||||
const frustrationsList = tabStates[currentTab]?.frustrationsList || [];
|
||||
const exceptionsList = tabStates[currentTab]?.exceptionsList || [];
|
||||
const resourceListUnmap = tabStates[currentTab]?.resourceList || [];
|
||||
const fetchList = tabStates[currentTab]?.fetchList || [];
|
||||
const graphqlList = tabStates[currentTab]?.graphqlList || [];
|
||||
const performanceChartData =
|
||||
tabStates[currentTab]?.performanceChartData || [];
|
||||
const tabValues = Object.values(tabStates);
|
||||
const dataSource = uiPlayerStore.dataSource;
|
||||
const showSingleTab = dataSource === 'current';
|
||||
|
||||
const {
|
||||
stackEventList = [],
|
||||
frustrationsList = [],
|
||||
exceptionsList = [],
|
||||
resourceListUnmap = [],
|
||||
fetchList = [],
|
||||
graphqlList = [],
|
||||
performanceChartData = [],
|
||||
} = React.useMemo(() => {
|
||||
if (showSingleTab) {
|
||||
const stackEventList = tabStates[currentTab].stackList;
|
||||
const frustrationsList = tabStates[currentTab].frustrationsList;
|
||||
const exceptionsList = tabStates[currentTab].exceptionsList;
|
||||
const resourceListUnmap = tabStates[currentTab].resourceList;
|
||||
const fetchList = tabStates[currentTab].fetchList;
|
||||
const graphqlList = tabStates[currentTab].graphqlList;
|
||||
const performanceChartData =
|
||||
tabStates[currentTab].performanceChartData;
|
||||
|
||||
return {
|
||||
stackEventList,
|
||||
frustrationsList,
|
||||
exceptionsList,
|
||||
resourceListUnmap,
|
||||
fetchList,
|
||||
graphqlList,
|
||||
performanceChartData,
|
||||
}
|
||||
} else {
|
||||
const stackEventList = tabValues.flatMap((tab) => tab.stackList);
|
||||
// these two are global
|
||||
const frustrationsList = tabValues[0].frustrationsList;
|
||||
const exceptionsList = tabValues[0].exceptionsList;
|
||||
// we can't compute global chart data because some tabs coexist
|
||||
const performanceChartData: any = [];
|
||||
const resourceListUnmap = tabValues.flatMap((tab) => tab.resourceList);
|
||||
const fetchList = tabValues.flatMap((tab) => tab.fetchList);
|
||||
const graphqlList = tabValues.flatMap((tab) => tab.graphqlList);
|
||||
|
||||
return {
|
||||
stackEventList,
|
||||
frustrationsList,
|
||||
exceptionsList,
|
||||
resourceListUnmap,
|
||||
fetchList,
|
||||
graphqlList,
|
||||
performanceChartData,
|
||||
}
|
||||
}
|
||||
}, [tabStates, currentTab, dataSource, tabValues]);
|
||||
|
||||
const fetchPresented = fetchList.length > 0;
|
||||
const resourceList = resourceListUnmap
|
||||
|
|
@ -168,7 +216,18 @@ function WebOverviewPanelCont() {
|
|||
PERFORMANCE: checkInZoomRange(performanceChartData),
|
||||
FRUSTRATIONS: checkInZoomRange(frustrationsList),
|
||||
};
|
||||
}, [tabStates, currentTab, zoomEnabled, zoomStartTs, zoomEndTs]);
|
||||
}, [
|
||||
tabStates,
|
||||
currentTab,
|
||||
zoomEnabled,
|
||||
zoomStartTs,
|
||||
zoomEndTs,
|
||||
resourceList.length,
|
||||
exceptionsList.length,
|
||||
stackEventList.length,
|
||||
performanceChartData.length,
|
||||
frustrationsList.length,
|
||||
]);
|
||||
|
||||
const originStr = window.env.ORIGIN || window.location.origin;
|
||||
const isSaas = /app\.openreplay\.com/.test(originStr);
|
||||
|
|
@ -187,6 +246,7 @@ function WebOverviewPanelCont() {
|
|||
sessionId={sessionId}
|
||||
setZoomTab={setZoomTab}
|
||||
zoomTab={zoomTab}
|
||||
showSingleTab={showSingleTab}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -238,6 +298,7 @@ function PanelComponent({
|
|||
spotTime,
|
||||
spotEndTime,
|
||||
onClose,
|
||||
showSingleTab,
|
||||
}: any) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
|
@ -280,12 +341,13 @@ function PanelComponent({
|
|||
) : null}
|
||||
</div>
|
||||
{isSpot ? null : (
|
||||
<div className="flex items-center h-20 mr-4 gap-2">
|
||||
<TimelineZoomButton />
|
||||
<div className="flex items-center h-20 mr-4 gap-3">
|
||||
<FeatureSelection
|
||||
list={selectedFeatures}
|
||||
updateList={setSelectedFeatures}
|
||||
/>
|
||||
{!isMobile ? <TabSelector /> : null}
|
||||
<TimelineZoomButton />
|
||||
</div>
|
||||
)}
|
||||
</BottomBlock.Header>
|
||||
|
|
@ -302,12 +364,19 @@ function PanelComponent({
|
|||
style={{ height: '60px', minHeight: 'unset', padding: 0 }}
|
||||
title={
|
||||
<div className="flex items-center">
|
||||
<Icon name="info-circle" className="mr-2" size="18" />
|
||||
<InfoCircleOutlined size={18} />
|
||||
Select a debug option to visualize on timeline.
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{isSpot ? <VerticalPointerLineComp time={spotTime} endTime={spotEndTime} /> : <VerticalPointerLine />}
|
||||
{isSpot ? (
|
||||
<VerticalPointerLineComp
|
||||
time={spotTime}
|
||||
endTime={spotEndTime}
|
||||
/>
|
||||
) : (
|
||||
<VerticalPointerLine />
|
||||
)}
|
||||
{selectedFeatures.map((feature: any, index: number) => (
|
||||
<div
|
||||
key={feature}
|
||||
|
|
@ -318,6 +387,7 @@ function PanelComponent({
|
|||
<EventRow
|
||||
isGraph={feature === 'PERFORMANCE'}
|
||||
title={feature}
|
||||
disabled={!isMobile && !showSingleTab}
|
||||
list={resources[feature]}
|
||||
renderElement={(pointer: any[], isGrouped: boolean) => (
|
||||
<TimelinePointer
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import { getTimelinePosition } from 'App/utils';
|
||||
import { Icon, Tooltip } from 'UI';
|
||||
import { Icon } from 'UI';
|
||||
import { InfoCircleOutlined} from '@ant-design/icons'
|
||||
import {Tooltip} from 'antd';
|
||||
import PerformanceGraph from '../PerformanceGraph';
|
||||
interface Props {
|
||||
list?: any[];
|
||||
|
|
@ -13,9 +15,10 @@ interface Props {
|
|||
isGraph?: boolean;
|
||||
zIndex?: number;
|
||||
noMargin?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
const EventRow = React.memo((props: Props) => {
|
||||
const { title, className, list = [], endTime = 0, isGraph = false, message = '' } = props;
|
||||
const { title, className, list = [], endTime = 0, isGraph = false, message = '', disabled } = props;
|
||||
const scale = 100 / endTime;
|
||||
const _list =
|
||||
isGraph ? [] :
|
||||
|
|
@ -82,7 +85,7 @@ const EventRow = React.memo((props: Props) => {
|
|||
}
|
||||
|
||||
return groupedItems;
|
||||
}, [list]);
|
||||
}, [list.length]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -91,21 +94,24 @@ const EventRow = React.memo((props: Props) => {
|
|||
>
|
||||
<div
|
||||
className={cn(
|
||||
'uppercase text-sm flex items-center py-1',
|
||||
'uppercase text-sm flex items-center py-1 gap-1',
|
||||
props.noMargin ? '' : 'ml-2'
|
||||
)}
|
||||
>
|
||||
<div
|
||||
style={{ zIndex: props.zIndex ? props.zIndex : undefined }}
|
||||
className="mr-2 leading-none"
|
||||
className="leading-none mt-0.5"
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
{message ? <RowInfo message={message} /> : null}
|
||||
|
||||
<Tooltip title={message} placement='left'>
|
||||
<InfoCircleOutlined className='text-neutral-400' />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="relative w-full" style={{ zIndex: props.zIndex ? props.zIndex : undefined }}>
|
||||
{isGraph ? (
|
||||
<PerformanceGraph list={list} />
|
||||
<PerformanceGraph disabled={disabled} list={list} />
|
||||
) : _list.length > 0 ? (
|
||||
_list.map((item: { items: any[], left: number, isGrouped: boolean }, index: number) => {
|
||||
const left = item.left
|
||||
|
|
@ -123,7 +129,7 @@ const EventRow = React.memo((props: Props) => {
|
|||
);
|
||||
})
|
||||
) : (
|
||||
<div className={cn('color-gray-medium text-sm', props.noMargin ? '' : 'ml-4')}>
|
||||
<div className={cn('color-gray-medium text-xs', props.noMargin ? '' : 'ml-2')}>
|
||||
None captured.
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -133,11 +139,3 @@ const EventRow = React.memo((props: Props) => {
|
|||
});
|
||||
|
||||
export default EventRow;
|
||||
|
||||
function RowInfo({ message }: any) {
|
||||
return (
|
||||
<Tooltip title={message} delay={0}>
|
||||
<Icon name="info-circle" color="gray-medium" />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import React from 'react';
|
||||
import { Popover, Checkbox } from 'antd';
|
||||
import { Popover, Checkbox, Button } from 'antd';
|
||||
import {EyeInvisibleOutlined} from '@ant-design/icons';
|
||||
import { Icon } from 'UI'
|
||||
import Funnel from '@/types/funnel';
|
||||
|
||||
const NETWORK = 'NETWORK';
|
||||
const ERRORS = 'ERRORS';
|
||||
|
|
@ -59,7 +61,7 @@ function FeatureSelection(props: Props) {
|
|||
<Popover
|
||||
trigger="click"
|
||||
content={
|
||||
<div>
|
||||
<div className='flex flex-col gap-3'>
|
||||
<div
|
||||
className={'flex items-center gap-2 cursor-pointer'}
|
||||
onClick={() => toggleAllFeatures()}
|
||||
|
|
@ -81,10 +83,9 @@ function FeatureSelection(props: Props) {
|
|||
</div>
|
||||
}
|
||||
>
|
||||
<div className={'font-semibold flex items-center gap-2 text-main cursor-pointer'}>
|
||||
<Icon size={16} name={'funnel'} color={'main'} />
|
||||
<div>X-Ray Events</div>
|
||||
</div>
|
||||
<Button color='primary' size='small' type='text' className={'font-medium'} icon={<EyeInvisibleOutlined size={12} />} >
|
||||
Hide / Show
|
||||
</Button>
|
||||
</Popover>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,82 +1,107 @@
|
|||
import React from 'react';
|
||||
import { AreaChart, Area, ResponsiveContainer } from 'recharts';
|
||||
import {InfoCircleOutlined} from '@ant-design/icons'
|
||||
|
||||
interface Props {
|
||||
list: any;
|
||||
list: any;
|
||||
disabled?: boolean;
|
||||
}
|
||||
const PerformanceGraph = React.memo((props: Props) => {
|
||||
const { list } = props;
|
||||
const { list, disabled } = props;
|
||||
|
||||
const finalValues = React.useMemo(() => {
|
||||
const cpuMax = list.reduce((acc: number, item: any) => {
|
||||
return Math.max(acc, item.cpu);
|
||||
}, 0);
|
||||
const cpuMin = list.reduce((acc: number, item: any) => {
|
||||
return Math.min(acc, item.cpu);
|
||||
}, Infinity);
|
||||
const finalValues = React.useMemo(() => {
|
||||
const cpuMax = list.reduce((acc: number, item: any) => {
|
||||
return Math.max(acc, item.cpu);
|
||||
}, 0);
|
||||
const cpuMin = list.reduce((acc: number, item: any) => {
|
||||
return Math.min(acc, item.cpu);
|
||||
}, Infinity);
|
||||
|
||||
const memoryMin = list.reduce((acc: number, item: any) => {
|
||||
return Math.min(acc, item.usedHeap);
|
||||
}, Infinity);
|
||||
const memoryMax = list.reduce((acc: number, item: any) => {
|
||||
return Math.max(acc, item.usedHeap);
|
||||
}, 0);
|
||||
const memoryMin = list.reduce((acc: number, item: any) => {
|
||||
return Math.min(acc, item.usedHeap);
|
||||
}, Infinity);
|
||||
const memoryMax = list.reduce((acc: number, item: any) => {
|
||||
return Math.max(acc, item.usedHeap);
|
||||
}, 0);
|
||||
|
||||
const convertToPercentage = (val: number, max: number, min: number) => {
|
||||
return ((val - min) / (max - min)) * 100;
|
||||
};
|
||||
const cpuValues = list.map((item: any) => convertToPercentage(item.cpu, cpuMax, cpuMin));
|
||||
const memoryValues = list.map((item: any) => convertToPercentage(item.usedHeap, memoryMax, memoryMin));
|
||||
const mergeArraysWithMaxNumber = (arr1: any[], arr2: any[]) => {
|
||||
const maxLength = Math.max(arr1.length, arr2.length);
|
||||
const result = [];
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
const num = Math.round(Math.max(arr1[i] || 0, arr2[i] || 0));
|
||||
result.push(num > 60 ? num : 1);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
const finalValues = mergeArraysWithMaxNumber(cpuValues, memoryValues);
|
||||
return finalValues;
|
||||
}, []);
|
||||
|
||||
const data = list.map((item: any, index: number) => {
|
||||
return {
|
||||
time: item.time,
|
||||
cpu: finalValues[index],
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<ResponsiveContainer height={35}>
|
||||
<AreaChart
|
||||
data={data}
|
||||
margin={{
|
||||
top: 0,
|
||||
right: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
>
|
||||
<defs>
|
||||
<linearGradient id="cpuGradientTimeline" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="30%" stopColor="#CC0000" stopOpacity={0.5} />
|
||||
<stop offset="95%" stopColor="#3EAAAF" stopOpacity={0.8} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
{/* <Tooltip filterNull={false} /> */}
|
||||
<Area
|
||||
dataKey="cpu"
|
||||
baseValue={5}
|
||||
type="monotone"
|
||||
stroke="none"
|
||||
activeDot={false}
|
||||
fill="url(#cpuGradientTimeline)"
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
const convertToPercentage = (val: number, max: number, min: number) => {
|
||||
return ((val - min) / (max - min)) * 100;
|
||||
};
|
||||
const cpuValues = list.map((item: any) =>
|
||||
convertToPercentage(item.cpu, cpuMax, cpuMin)
|
||||
);
|
||||
const memoryValues = list.map((item: any) =>
|
||||
convertToPercentage(item.usedHeap, memoryMax, memoryMin)
|
||||
);
|
||||
const mergeArraysWithMaxNumber = (arr1: any[], arr2: any[]) => {
|
||||
const maxLength = Math.max(arr1.length, arr2.length);
|
||||
const result = [];
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
const num = Math.round(Math.max(arr1[i] || 0, arr2[i] || 0));
|
||||
result.push(num > 60 ? num : 1);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
const finalValues = mergeArraysWithMaxNumber(cpuValues, memoryValues);
|
||||
return finalValues;
|
||||
}, [list.length]);
|
||||
|
||||
const data = list.map((item: any, index: number) => {
|
||||
return {
|
||||
time: item.time,
|
||||
cpu: finalValues[index],
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={'relative'}>
|
||||
{disabled ? (
|
||||
<div
|
||||
className={
|
||||
'flex justify-center'
|
||||
}
|
||||
>
|
||||
<div className={'text-xs text-neutral-400 ps-2'}>
|
||||
Multi-tab performance overview is not available.
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<ResponsiveContainer height={35}>
|
||||
<AreaChart
|
||||
data={data}
|
||||
margin={{
|
||||
top: 0,
|
||||
right: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="cpuGradientTimeline"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="0"
|
||||
y2="1"
|
||||
>
|
||||
<stop offset="30%" stopColor="#CC0000" stopOpacity={0.5} />
|
||||
<stop offset="95%" stopColor="#3EAAAF" stopOpacity={0.8} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
{/* <Tooltip filterNull={false} /> */}
|
||||
<Area
|
||||
dataKey="cpu"
|
||||
baseValue={5}
|
||||
type="monotone"
|
||||
stroke="none"
|
||||
activeDot={false}
|
||||
fill="url(#cpuGradientTimeline)"
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default PerformanceGraph;
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ export function FrustrationElement({ item, createEventClickHandler }: CommonProp
|
|||
const elData = getFrustration(item);
|
||||
return (
|
||||
<Tooltip
|
||||
placement={'right'}
|
||||
placement={'top'}
|
||||
title={
|
||||
<div className="">
|
||||
<b>{elData.name}</b>
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ function GroupedIssue({
|
|||
<div
|
||||
onClick={onClick}
|
||||
className={
|
||||
'h-5 w-5 cursor-pointer rounded-full bg-red text-white font-bold flex items-center justify-center text-sm'
|
||||
'h-5 w-5 cursor-pointer rounded-full bg-red text-white font-bold flex items-center justify-center text-xs'
|
||||
}
|
||||
>
|
||||
{items.length}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { Loader, Icon } from 'UI';
|
||||
import { Loader } from 'UI';
|
||||
import {Button, Tooltip} from 'antd';
|
||||
import {CloseOutlined} from '@ant-design/icons';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useStore } from 'App/mstore';
|
||||
import SelectorsList from './components/SelectorsList/SelectorsList';
|
||||
import { PlayerContext } from 'App/components/Session/playerContext';
|
||||
import { compareJsonObjects } from 'App/utils';
|
||||
|
||||
import Select from 'Shared/Select';
|
||||
import {Select, Form} from 'antd';
|
||||
|
||||
const JUMP_OFFSET = 1000;
|
||||
interface Props {
|
||||
|
|
@ -58,34 +60,29 @@ function PageInsightsPanel({ setActiveTab }: Props) {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 bg-white">
|
||||
<div className="pb-3 flex items-center" style={{ maxWidth: '241px', paddingTop: '5px' }}>
|
||||
<div className="flex items-center">
|
||||
<span className="mr-1 text-xl">Clicks</span>
|
||||
</div>
|
||||
<div
|
||||
onClick={() => {
|
||||
setActiveTab('');
|
||||
}}
|
||||
className="ml-auto flex items-center justify-center bg-white cursor-pointer"
|
||||
>
|
||||
<Icon name="close" size="18" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-4 flex items-center">
|
||||
<div className="mr-2 flex-shrink-0">In Page</div>
|
||||
<Select
|
||||
isSearchable={true}
|
||||
right
|
||||
placeholder="change"
|
||||
options={urlOptions}
|
||||
name="url"
|
||||
defaultValue={defaultValue}
|
||||
onChange={onPageSelect}
|
||||
id="change-dropdown"
|
||||
className="w-full"
|
||||
style={{ width: '100%' }}
|
||||
<div className="p-2 py-4 bg-white">
|
||||
<div className="flex items-center gap-2 mb-3 overflow-hidden">
|
||||
<div className="flex-shrink-0 font-medium">Page</div>
|
||||
<Form.Item name="url" className='mb-0 w-[176px]'>
|
||||
<Select
|
||||
showSearch
|
||||
placeholder="change"
|
||||
options={urlOptions}
|
||||
defaultValue={defaultValue}
|
||||
onChange={onPageSelect}
|
||||
id="change-dropdown"
|
||||
className="w-full rounded-lg max-w-[270px]"
|
||||
dropdownStyle={{ }}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Tooltip title="Close Panel" placement='bottomRight'>
|
||||
<Button
|
||||
className="ml-2"
|
||||
type='text'
|
||||
onClick={() => { setActiveTab(''); }}
|
||||
icon={<CloseOutlined />}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Loader loading={loading}>
|
||||
<SelectorsList />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
.wrapper {
|
||||
padding: 10px;
|
||||
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 3px;
|
||||
padding: 1rem;
|
||||
background-color: $gray-lightest;
|
||||
margin-bottom: 15px;
|
||||
|
||||
|
|
@ -18,8 +16,6 @@
|
|||
border-radius: 10px;
|
||||
background-color: $tealx;
|
||||
flex-shrink: 0;
|
||||
border: solid thin white;
|
||||
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
|
|
|||
|
|
@ -17,20 +17,20 @@ export default function SelectorCard({ index = 1, target, showContent }: Props)
|
|||
|
||||
return (
|
||||
// @ts-ignore TODO for Alex
|
||||
<div className={cn(stl.wrapper, { [stl.active]: showContent })} onClick={() => activeTarget(index)}>
|
||||
<div className={cn(stl.wrapper, 'rounded-xl', { [stl.active]: showContent })} onClick={() => activeTarget(index)}>
|
||||
<div className={stl.top}>
|
||||
{/* @ts-ignore */}
|
||||
<Tooltip position="top" title="Rank of the most clicked element">
|
||||
<div className={stl.index}>{index + 1}</div>
|
||||
</Tooltip>
|
||||
<div className="truncate">{target.selector}</div>
|
||||
<div className="truncate font-mono">{target.selector}</div>
|
||||
</div>
|
||||
{showContent && (
|
||||
<div className={stl.counts}>
|
||||
<div>
|
||||
{target.count} Clicks - {target.percent}%
|
||||
{target.count} Click{target.count > 1 ? 's' : ''} - {target.percent}%
|
||||
</div>
|
||||
<div className="color-gray-medium">TOTAL CLICKS</div>
|
||||
<div className="text-neutral-400">TOTAL CLICKS</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -17,12 +17,14 @@ import {
|
|||
} from 'recharts';
|
||||
import { durationFromMsFormatted } from 'App/date';
|
||||
import { formatBytes } from 'App/utils';
|
||||
import {Tooltip as TooltipANT} from 'antd';
|
||||
|
||||
import stl from './performance.module.css';
|
||||
|
||||
import BottomBlock from '../BottomBlock';
|
||||
import InfoLine from '../BottomBlock/InfoLine';
|
||||
import { useStore } from 'App/mstore'
|
||||
import { Segmented } from 'antd'
|
||||
|
||||
const CPU_VISUAL_OFFSET = 10;
|
||||
|
||||
|
|
@ -457,15 +459,33 @@ function Performance() {
|
|||
return (
|
||||
<BottomBlock>
|
||||
<BottomBlock.Header>
|
||||
<div className="flex items-center w-full">
|
||||
<div className="font-semibold color-gray-medium mr-auto">Performance</div>
|
||||
<InfoLine>
|
||||
<InfoLine.Point
|
||||
label="Device Heap Size"
|
||||
value={formatBytes(userDeviceHeapSize)}
|
||||
display={true}
|
||||
/>
|
||||
</InfoLine>
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<div className="flex gap-3 items-center">
|
||||
<div className="font-semibold color-gray-medium mr-auto">Performance</div>
|
||||
<InfoLine>
|
||||
<InfoLine.Point
|
||||
label="Device Heap Size"
|
||||
value={formatBytes(userDeviceHeapSize)}
|
||||
display={true}
|
||||
/>
|
||||
</InfoLine>
|
||||
</div>
|
||||
|
||||
<div className={'flex items-center gap-3'}>
|
||||
<Segmented
|
||||
options={[
|
||||
{ label: (
|
||||
<TooltipANT title="Performance overview isn't supported across tabs.">
|
||||
<span>All Tabs</span>
|
||||
</TooltipANT>
|
||||
), value: 'all', disabled: true, },
|
||||
{ label: 'Current Tab', value: 'current' },
|
||||
]}
|
||||
defaultValue="current"
|
||||
size="small"
|
||||
className="rounded-full font-medium"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BottomBlock.Header>
|
||||
<BottomBlock.Content>
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import { Icon } from 'UI';
|
|||
import LogsButton from 'App/components/Session/Player/SharedComponents/BackendLogs/LogsButton';
|
||||
|
||||
import ControlButton from './ControlButton';
|
||||
import { WebEventsList } from "./EventsList";
|
||||
import Timeline from './Timeline';
|
||||
import PlayerControls from './components/PlayerControls';
|
||||
import styles from './controls.module.css';
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@ import { PlayerContext, MobilePlayerContext } from 'Components/Session/playerCon
|
|||
import { observer } from 'mobx-react-lite';
|
||||
import { getTimelinePosition } from './getTimelinePosition'
|
||||
|
||||
function EventsList({ scale }: { scale: number }) {
|
||||
function EventsList() {
|
||||
const { store } = useContext(PlayerContext);
|
||||
|
||||
const { tabStates, eventCount } = store.get();
|
||||
const { eventCount, endTime } = store.get();
|
||||
const tabStates = store.get().tabStates;
|
||||
const scale = 100 / endTime;
|
||||
const events = React.useMemo(() => {
|
||||
return Object.values(tabStates)[0]?.eventList.filter(e => e.time) || [];
|
||||
}, [eventCount]);
|
||||
|
|
@ -34,11 +36,12 @@ function EventsList({ scale }: { scale: number }) {
|
|||
);
|
||||
}
|
||||
|
||||
function MobileEventsList({ scale }: { scale: number }) {
|
||||
function MobileEventsList() {
|
||||
const { store } = useContext(MobilePlayerContext);
|
||||
const { eventList } = store.get();
|
||||
const { eventList, endTime } = store.get();
|
||||
const events = eventList.filter(e => e.type !== 'SWIPE')
|
||||
|
||||
const scale = 100/endTime;
|
||||
return (
|
||||
<>
|
||||
{events.map((e) => (
|
||||
|
|
|
|||
|
|
@ -13,11 +13,7 @@ import NotesList from './NotesList';
|
|||
import SkipIntervalsList from './SkipIntervalsList';
|
||||
import TimelineTracker from 'Components/Session_/Player/Controls/TimelineTracker';
|
||||
|
||||
interface IProps {
|
||||
isMobile?: boolean;
|
||||
}
|
||||
|
||||
function Timeline(props: IProps) {
|
||||
function Timeline({ isMobile }: { isMobile: boolean }) {
|
||||
const { player, store } = useContext(PlayerContext);
|
||||
const [wasPlaying, setWasPlaying] = useState(false);
|
||||
const [maxWidth, setMaxWidth] = useState(0);
|
||||
|
|
@ -158,7 +154,7 @@ function Timeline(props: IProps) {
|
|||
{devtoolsLoading || domLoading || !ready ? <div className={stl.stripes} /> : null}
|
||||
</div>
|
||||
|
||||
{props.isMobile ? <MobEventsList scale={scale} /> : <WebEventsList scale={scale} />}
|
||||
{isMobile ? <MobEventsList /> : <WebEventsList />}
|
||||
<NotesList scale={scale} />
|
||||
<SkipIntervalsList scale={scale} />
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ function TimelineZoomButton() {
|
|||
}, [])
|
||||
return (
|
||||
<Tooltip title="Select a portion of the timeline to view the x-ray and activity for that specific selection." placement='top'>
|
||||
<Button onClick={onClickHandler} size={'small'} className={'flex items-center font-semibold'}>
|
||||
<Button onClick={onClickHandler} size={'small'} className={'flex items-center font-medium'}>
|
||||
Focus Mode: {enabled ? 'On' : 'Off'}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,24 @@
|
|||
import React from 'react';
|
||||
import { useStore } from 'App/mstore'
|
||||
import { useStore } from 'App/mstore';
|
||||
import { PlayerContext } from 'App/components/Session/playerContext';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { JSONTree, NoContent, Tooltip } from 'UI';
|
||||
import { formatMs } from 'App/date';
|
||||
import diff from 'microdiff'
|
||||
import { STORAGE_TYPES, selectStorageList, selectStorageListNow, selectStorageType } from 'Player';
|
||||
import diff from 'microdiff';
|
||||
import {
|
||||
STORAGE_TYPES,
|
||||
selectStorageList,
|
||||
selectStorageListNow,
|
||||
selectStorageType,
|
||||
} from 'Player';
|
||||
import Autoscroll from '../Autoscroll';
|
||||
import BottomBlock from '../BottomBlock/index';
|
||||
import DiffRow from './DiffRow';
|
||||
import cn from 'classnames';
|
||||
import stl from './storage.module.css';
|
||||
import logger from "App/logger";
|
||||
import ReduxViewer from './ReduxViewer'
|
||||
import logger from 'App/logger';
|
||||
import ReduxViewer from './ReduxViewer';
|
||||
import { Segmented } from 'antd'
|
||||
|
||||
function getActionsName(type: string) {
|
||||
switch (type) {
|
||||
|
|
@ -31,7 +37,7 @@ const storageDecodeKeys = {
|
|||
[STORAGE_TYPES.ZUSTAND]: ['state', 'mutation'],
|
||||
[STORAGE_TYPES.MOBX]: ['payload'],
|
||||
[STORAGE_TYPES.NONE]: ['state, action', 'payload', 'mutation'],
|
||||
}
|
||||
};
|
||||
|
||||
function Storage() {
|
||||
const { uiPlayerStore } = useStore();
|
||||
|
|
@ -42,49 +48,48 @@ function Storage() {
|
|||
const [stateObject, setState] = React.useState({});
|
||||
|
||||
const { player, store } = React.useContext(PlayerContext);
|
||||
const { tabStates, currentTab } = store.get()
|
||||
const state = tabStates[currentTab] || {}
|
||||
const { tabStates, currentTab } = store.get();
|
||||
const state = tabStates[currentTab] || {};
|
||||
|
||||
const listNow = selectStorageListNow(state) || [];
|
||||
const list = selectStorageList(state) || [];
|
||||
const type = selectStorageType(state) || STORAGE_TYPES.NONE
|
||||
const type = selectStorageType(state) || STORAGE_TYPES.NONE;
|
||||
|
||||
React.useEffect(() => {
|
||||
let currentState;
|
||||
if (listNow.length === 0) {
|
||||
currentState = decodeMessage(list[0])
|
||||
currentState = decodeMessage(list[0]);
|
||||
} else {
|
||||
currentState = decodeMessage(listNow[listNow.length - 1])
|
||||
currentState = decodeMessage(listNow[listNow.length - 1]);
|
||||
}
|
||||
const stateObj = currentState?.state || currentState?.payload?.state || {}
|
||||
const stateObj = currentState?.state || currentState?.payload?.state || {};
|
||||
const newState = Object.assign(stateObject, stateObj);
|
||||
setState(newState);
|
||||
|
||||
}, [listNow.length]);
|
||||
|
||||
const decodeMessage = (msg: any) => {
|
||||
const decoded = {};
|
||||
const pureMSG = { ...msg }
|
||||
const pureMSG = { ...msg };
|
||||
const keys = storageDecodeKeys[type];
|
||||
try {
|
||||
keys.forEach(key => {
|
||||
keys.forEach((key) => {
|
||||
if (pureMSG[key]) {
|
||||
// @ts-ignore TODO: types for decoder
|
||||
decoded[key] = player.decodeMessage(pureMSG[key]);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error("Error on message decoding: ", e, pureMSG);
|
||||
logger.error('Error on message decoding: ', e, pureMSG);
|
||||
return null;
|
||||
}
|
||||
return { ...pureMSG, ...decoded };
|
||||
}
|
||||
};
|
||||
|
||||
const decodedList = React.useMemo(() => {
|
||||
return listNow.map(msg => {
|
||||
return decodeMessage(msg)
|
||||
})
|
||||
}, [listNow.length])
|
||||
return listNow.map((msg) => {
|
||||
return decodeMessage(msg);
|
||||
});
|
||||
}, [listNow.length]);
|
||||
|
||||
const focusNextButton = () => {
|
||||
if (lastBtnRef.current) {
|
||||
|
|
@ -99,7 +104,10 @@ function Storage() {
|
|||
focusNextButton();
|
||||
}, [listNow]);
|
||||
|
||||
const renderDiff = (item: Record<string, any>, prevItem?: Record<string, any>) => {
|
||||
const renderDiff = (
|
||||
item: Record<string, any>,
|
||||
prevItem?: Record<string, any>
|
||||
) => {
|
||||
if (!showDiffs) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -113,7 +121,10 @@ function Storage() {
|
|||
|
||||
if (!stateDiff) {
|
||||
return (
|
||||
<div style={{ flex: 3 }} className="flex flex-col p-2 pr-0 font-mono text-disabled-text">
|
||||
<div
|
||||
style={{ flex: 3 }}
|
||||
className="flex flex-col p-2 pr-0 font-mono text-disabled-text"
|
||||
>
|
||||
No diff
|
||||
</div>
|
||||
);
|
||||
|
|
@ -121,13 +132,15 @@ function Storage() {
|
|||
|
||||
return (
|
||||
<div style={{ flex: 3 }} className="flex flex-col p-1 font-mono">
|
||||
{stateDiff.map((d: Record<string, any>, i: number) => renderDiffs(d, i))}
|
||||
{stateDiff.map((d: Record<string, any>, i: number) =>
|
||||
renderDiffs(d, i)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderDiffs = (diff: Record<string, any>, i: number) => {
|
||||
const path = diff.path.join('.')
|
||||
const path = diff.path.join('.');
|
||||
return (
|
||||
<React.Fragment key={i}>
|
||||
<DiffRow path={path} diff={diff} />
|
||||
|
|
@ -145,12 +158,16 @@ function Storage() {
|
|||
player.jump(list[listNow.length].time);
|
||||
};
|
||||
|
||||
const renderItem = (item: Record<string, any>, i: number, prevItem?: Record<string, any>) => {
|
||||
const renderItem = (
|
||||
item: Record<string, any>,
|
||||
i: number,
|
||||
prevItem?: Record<string, any>
|
||||
) => {
|
||||
let src;
|
||||
let name;
|
||||
|
||||
const itemD = item
|
||||
const prevItemD = prevItem ? prevItem : undefined
|
||||
const itemD = item;
|
||||
const prevItemD = prevItem ? prevItem : undefined;
|
||||
|
||||
switch (type) {
|
||||
case STORAGE_TYPES.REDUX:
|
||||
|
|
@ -177,7 +194,10 @@ function Storage() {
|
|||
|
||||
return (
|
||||
<div
|
||||
className={cn('flex justify-between items-start', src !== null ? 'border-b' : '')}
|
||||
className={cn(
|
||||
'flex justify-between items-start',
|
||||
src !== null ? 'border-b' : ''
|
||||
)}
|
||||
key={`store-${i}`}
|
||||
>
|
||||
{src === null ? (
|
||||
|
|
@ -187,7 +207,10 @@ function Storage() {
|
|||
) : (
|
||||
<>
|
||||
{renderDiff(itemD, prevItemD)}
|
||||
<div style={{ flex: 2 }} className={cn("flex pt-2", showDiffs && 'pl-10')}>
|
||||
<div
|
||||
style={{ flex: 2 }}
|
||||
className={cn('flex pt-2', showDiffs && 'pl-10')}
|
||||
>
|
||||
<JSONTree
|
||||
name={ensureString(name)}
|
||||
src={src}
|
||||
|
|
@ -202,11 +225,16 @@ function Storage() {
|
|||
className="flex-1 flex gap-2 pt-2 items-center justify-end self-start"
|
||||
>
|
||||
{typeof item?.duration === 'number' && (
|
||||
<div className="font-size-12 color-gray-medium">{formatMs(itemD.duration)}</div>
|
||||
<div className="font-size-12 color-gray-medium">
|
||||
{formatMs(itemD.duration)}
|
||||
</div>
|
||||
)}
|
||||
<div className="w-12">
|
||||
{i + 1 < listNow.length && (
|
||||
<button className={stl.button} onClick={() => player.jump(item.time)}>
|
||||
<button
|
||||
className={stl.button}
|
||||
onClick={() => player.jump(item.time)}
|
||||
>
|
||||
{'JUMP'}
|
||||
</button>
|
||||
)}
|
||||
|
|
@ -222,31 +250,36 @@ function Storage() {
|
|||
};
|
||||
|
||||
if (type === STORAGE_TYPES.REDUX) {
|
||||
return <ReduxViewer />
|
||||
return <ReduxViewer />;
|
||||
}
|
||||
return (
|
||||
<BottomBlock>
|
||||
{/*@ts-ignore*/}
|
||||
<>
|
||||
<BottomBlock.Header>
|
||||
{list.length > 0 && (
|
||||
<div className="flex w-full">
|
||||
<h3 style={{ width: '25%', marginRight: 20 }} className="font-semibold">
|
||||
{'STATE'}
|
||||
</h3>
|
||||
{showDiffs ? (
|
||||
<h3 style={{ width: '39%' }} className="font-semibold">
|
||||
DIFFS
|
||||
</h3>
|
||||
) : null}
|
||||
<h3 style={{ width: '30%' }} className="font-semibold">
|
||||
{getActionsName(type)}
|
||||
</h3>
|
||||
<h3 style={{ paddingRight: 30, marginLeft: 'auto' }} className="font-semibold">
|
||||
<Tooltip title="Time to execute">TTE</Tooltip>
|
||||
</h3>
|
||||
<div className="flex w-full items-center">
|
||||
<div
|
||||
style={{ width: '25%', marginRight: 20 }}
|
||||
className="font-semibold flex items-center gap-2"
|
||||
>
|
||||
<h3>{'STATE'}</h3>
|
||||
</div>
|
||||
)}
|
||||
{showDiffs ? (
|
||||
<h3 style={{ width: '39%' }} className="font-semibold">
|
||||
DIFFS
|
||||
</h3>
|
||||
) : null}
|
||||
<h3 style={{ width: '30%' }} className="font-semibold">
|
||||
{getActionsName(type)}
|
||||
</h3>
|
||||
<h3
|
||||
style={{ paddingRight: 30, marginLeft: 'auto' }}
|
||||
className="font-semibold"
|
||||
>
|
||||
<Tooltip title="Time to execute">TTE</Tooltip>
|
||||
</h3>
|
||||
<Segmented options={[{ label: 'Current Tab', value: 'all' }]} />
|
||||
</div>
|
||||
</BottomBlock.Header>
|
||||
<BottomBlock.Content className="flex">
|
||||
<NoContent
|
||||
|
|
@ -307,7 +340,10 @@ function Storage() {
|
|||
.
|
||||
<br />
|
||||
<br />
|
||||
<button className="color-teal" onClick={() => hideHint('storage')}>
|
||||
<button
|
||||
className="color-teal"
|
||||
onClick={() => hideHint('storage')}
|
||||
>
|
||||
Got It!
|
||||
</button>
|
||||
</>
|
||||
|
|
@ -322,8 +358,7 @@ function Storage() {
|
|||
{'Empty state.'}
|
||||
</div>
|
||||
) : (
|
||||
<JSONTree collapsed={2} src={stateObject}
|
||||
/>
|
||||
<JSONTree collapsed={2} src={stateObject} />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex" style={{ width: '75%' }}>
|
||||
|
|
@ -342,7 +377,6 @@ function Storage() {
|
|||
|
||||
export default observer(Storage);
|
||||
|
||||
|
||||
/**
|
||||
* TODO: compute diff and only decode the required parts
|
||||
* WIP example
|
||||
|
|
@ -384,4 +418,4 @@ export default observer(Storage);
|
|||
* }, [list.length])
|
||||
* }
|
||||
*
|
||||
* */
|
||||
* */
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ function SpotConsole({ onClose }: { onClose: () => void }) {
|
|||
jump={jump}
|
||||
iconProps={getIconProps(log.level)}
|
||||
renderWithNL={renderWithNL}
|
||||
showSingleTab
|
||||
/>
|
||||
))}
|
||||
</VList>
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ function SpotPlayerHeader({
|
|||
{browserVersion && (
|
||||
<>
|
||||
<div>·</div>
|
||||
<div className="capitalize">Chrome v{browserVersion}</div>
|
||||
<div>Chromium v{browserVersion}</div>
|
||||
</>
|
||||
)}
|
||||
{resolution && (
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@ import cn from 'classnames';
|
|||
import cls from './infoLine.module.css';
|
||||
|
||||
const InfoLine = ({ children }) => (
|
||||
<div className={ cls.info }>
|
||||
<div className={ cn(cls.info, 'text-sm')}>
|
||||
{ children }
|
||||
</div>
|
||||
)
|
||||
|
||||
const Point = ({ label = '', value = '', display=true, color, dotColor }) => display
|
||||
? <div className={ cls.infoPoint } style={{ color }}>
|
||||
? <div className={ cn(cls.infoPoint, 'text-sm') } style={{ color }}>
|
||||
{ dotColor != null && <div className={ cn(cls.dot, `bg-${dotColor}`) } /> }
|
||||
<span className={cls.label}>{ `${label}` }</span> { value }
|
||||
<span className={cn(cls.label, 'text-sm')}>{ `${label}` }</span> { value }
|
||||
</div>
|
||||
: null;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import React, { useEffect, useRef, useState, useMemo } from 'react';
|
||||
import { LogLevel, ILog } from 'Player';
|
||||
import BottomBlock from '../BottomBlock';
|
||||
import { Tabs, Input, Icon, NoContent } from 'UI';
|
||||
import { Tabs, Icon, NoContent } from 'UI';
|
||||
import {Input} from 'antd';
|
||||
import {SearchOutlined, InfoCircleOutlined} from '@ant-design/icons';
|
||||
import cn from 'classnames';
|
||||
import ConsoleRow from '../ConsoleRow';
|
||||
import { PlayerContext } from 'App/components/Session/playerContext';
|
||||
|
|
@ -9,6 +11,7 @@ import { observer } from 'mobx-react-lite';
|
|||
import { useStore } from 'App/mstore';
|
||||
import ErrorDetailsModal from 'App/components/Dashboard/components/Errors/ErrorDetailsModal';
|
||||
import { useModal } from 'App/components/Modal';
|
||||
import TabSelector from "../TabSelector";
|
||||
import useAutoscroll, { getLastItemTime } from '../useAutoscroll';
|
||||
import { useRegExListFilterMemo, useTabListFilterMemo } from '../useListFilter';
|
||||
import { VList, VListHandle } from "virtua";
|
||||
|
|
@ -93,6 +96,7 @@ function ConsolePanel({
|
|||
sessionStore: { devTools },
|
||||
uiPlayerStore,
|
||||
} = useStore();
|
||||
|
||||
const zoomEnabled = uiPlayerStore.timelineZoom.enabled;
|
||||
const zoomStartTs = uiPlayerStore.timelineZoom.startTs;
|
||||
const zoomEndTs = uiPlayerStore.timelineZoom.endTs;
|
||||
|
|
@ -109,29 +113,34 @@ function ConsolePanel({
|
|||
const jump = (t: number) => player.jump(t);
|
||||
|
||||
const { currentTab, tabStates } = store.get();
|
||||
const {
|
||||
logList = [],
|
||||
exceptionsList = [],
|
||||
logListNow = [],
|
||||
exceptionsListNow = [],
|
||||
} = tabStates[currentTab] ?? {};
|
||||
const tabsArr = Object.keys(tabStates);
|
||||
const tabValues = Object.values(tabStates);
|
||||
const dataSource = uiPlayerStore.dataSource;
|
||||
const showSingleTab = dataSource === 'current';
|
||||
const { logList = [], exceptionsList = [], logListNow = [], exceptionsListNow = [] } = React.useMemo(() => {
|
||||
if (showSingleTab) {
|
||||
return tabStates[currentTab] ?? {};
|
||||
} else {
|
||||
const logList = tabValues.flatMap(tab => tab.logList);
|
||||
const exceptionsList = tabValues.flatMap(tab => tab.exceptionsList);
|
||||
const logListNow = isLive ? tabValues.flatMap(tab => tab.logListNow) : [];
|
||||
const exceptionsListNow = isLive ? tabValues.flatMap(tab => tab.exceptionsListNow) : [];
|
||||
return { logList, exceptionsList, logListNow, exceptionsListNow }
|
||||
}
|
||||
}, [currentTab, tabStates, dataSource, tabValues, isLive])
|
||||
const getTabNum = (tab: string) => (tabsArr.findIndex((t) => t === tab) + 1);
|
||||
|
||||
const list = isLive
|
||||
? (useMemo(
|
||||
() => logListNow.concat(exceptionsListNow).sort((a, b) => a.time - b.time),
|
||||
[logListNow.length, exceptionsListNow.length]
|
||||
) as ILog[])
|
||||
: (useMemo(
|
||||
() => logList.concat(exceptionsList).sort((a, b) => a.time - b.time),
|
||||
[logList.length, exceptionsList.length]
|
||||
).filter((l) =>
|
||||
zoomEnabled ? l.time >= zoomStartTs && l.time <= zoomEndTs : true
|
||||
) as ILog[]);
|
||||
const list = useMemo(() => {
|
||||
if (isLive) {
|
||||
return logListNow.concat(exceptionsListNow).sort((a, b) => a.time - b.time)
|
||||
} else {
|
||||
const logs = logList.concat(exceptionsList).sort((a, b) => a.time - b.time)
|
||||
return zoomEnabled ? logs.filter(l => l.time >= zoomStartTs && l.time <= zoomEndTs) : logs
|
||||
}
|
||||
}, [isLive, logList.length, exceptionsList.length, logListNow.length, exceptionsListNow.length, zoomEnabled, zoomStartTs, zoomEndTs])
|
||||
let filteredList = useRegExListFilterMemo(list, (l) => l.value, filter);
|
||||
filteredList = useTabListFilterMemo(filteredList, (l) => LEVEL_TAB[l.level], ALL, activeTab);
|
||||
|
||||
React.useEffect(() => {
|
||||
}, [activeTab, filter]);
|
||||
const onTabClick = (activeTab: any) => devTools.update(INDEX_KEY, { activeTab });
|
||||
const onFilterChange = ({ target: { value } }: any) =>
|
||||
devTools.update(INDEX_KEY, { filter: value });
|
||||
|
|
@ -180,23 +189,26 @@ function ConsolePanel({
|
|||
<span className="font-semibold color-gray-medium mr-4">Console</span>
|
||||
<Tabs tabs={TABS} active={activeTab} onClick={onTabClick} border={false} />
|
||||
</div>
|
||||
<Input
|
||||
className="input-small h-8"
|
||||
placeholder="Filter by keyword"
|
||||
icon="search"
|
||||
name="filter"
|
||||
height={28}
|
||||
onChange={onFilterChange}
|
||||
value={filter}
|
||||
/>
|
||||
<div className={'flex items-center gap-2'}>
|
||||
<TabSelector />
|
||||
<Input
|
||||
className="rounded-lg"
|
||||
placeholder="Filter by keyword"
|
||||
name="filter"
|
||||
onChange={onFilterChange}
|
||||
value={filter}
|
||||
size='small'
|
||||
prefix={<SearchOutlined className='text-neutral-400' />}
|
||||
/>
|
||||
</div>
|
||||
{/* @ts-ignore */}
|
||||
</BottomBlock.Header>
|
||||
{/* @ts-ignore */}
|
||||
<BottomBlock.Content className="overflow-y-auto">
|
||||
<NoContent
|
||||
title={
|
||||
<div className="capitalize flex items-center mt-16">
|
||||
<Icon name="info-circle" className="mr-2" size="18" />
|
||||
<div className="capitalize flex items-center mt-16 gap-2">
|
||||
<InfoCircleOutlined size={18} />
|
||||
No Data
|
||||
</div>
|
||||
}
|
||||
|
|
@ -211,6 +223,8 @@ function ConsolePanel({
|
|||
iconProps={getIconProps(log.level)}
|
||||
renderWithNL={renderWithNL}
|
||||
onClick={() => showDetails(log)}
|
||||
showSingleTab={showSingleTab}
|
||||
getTabNum={getTabNum}
|
||||
/>
|
||||
))}
|
||||
</VList>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { LogLevel, ILog } from 'Player';
|
||||
import BottomBlock from '../BottomBlock';
|
||||
import { Tabs, Input, Icon, NoContent } from 'UI';
|
||||
import { Tabs, Input, NoContent } from 'UI';
|
||||
import cn from 'classnames';
|
||||
import ConsoleRow from '../ConsoleRow';
|
||||
import { IOSPlayerContext, MobilePlayerContext } from 'App/components/Session/playerContext';
|
||||
import {
|
||||
IOSPlayerContext,
|
||||
MobilePlayerContext,
|
||||
} from 'App/components/Session/playerContext';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { VList, VListHandle } from 'virtua';
|
||||
import { useStore } from 'App/mstore';
|
||||
|
|
@ -12,6 +15,7 @@ import ErrorDetailsModal from 'App/components/Dashboard/components/Errors/ErrorD
|
|||
import { useModal } from 'App/components/Modal';
|
||||
import useAutoscroll, { getLastItemTime } from '../useAutoscroll';
|
||||
import { useRegExListFilterMemo, useTabListFilterMemo } from '../useListFilter';
|
||||
import { InfoCircleOutlined, SearchOutlined } from '@ant-design/icons';
|
||||
|
||||
const ALL = 'ALL';
|
||||
const INFO = 'INFO';
|
||||
|
|
@ -26,7 +30,10 @@ const LEVEL_TAB = {
|
|||
[LogLevel.EXCEPTION]: ERRORS,
|
||||
} as const;
|
||||
|
||||
const TABS = [ALL, ERRORS, WARNINGS, INFO].map((tab) => ({ text: tab, key: tab }));
|
||||
const TABS = [ALL, ERRORS, WARNINGS, INFO].map((tab) => ({
|
||||
text: tab,
|
||||
key: tab,
|
||||
}));
|
||||
|
||||
function renderWithNL(s: string | null = '') {
|
||||
if (typeof s !== 'string') return '';
|
||||
|
|
@ -73,20 +80,23 @@ function MobileConsolePanel() {
|
|||
const [isDetailsModalActive, setIsDetailsModalActive] = useState(false);
|
||||
const { showModal } = useModal();
|
||||
|
||||
const { player, store } = React.useContext<IOSPlayerContext>(MobilePlayerContext);
|
||||
const { player, store } =
|
||||
React.useContext<IOSPlayerContext>(MobilePlayerContext);
|
||||
const jump = (t: number) => player.jump(t);
|
||||
|
||||
const {
|
||||
logList,
|
||||
logListNow,
|
||||
exceptionsListNow,
|
||||
} = store.get();
|
||||
const { logList, logListNow, exceptionsListNow } = store.get();
|
||||
|
||||
const list = logList as ILog[];
|
||||
let filteredList = useRegExListFilterMemo(list, (l) => l.value, filter);
|
||||
filteredList = useTabListFilterMemo(filteredList, (l) => LEVEL_TAB[l.level], ALL, activeTab);
|
||||
filteredList = useTabListFilterMemo(
|
||||
filteredList,
|
||||
(l) => LEVEL_TAB[l.level],
|
||||
ALL,
|
||||
activeTab
|
||||
);
|
||||
|
||||
const onTabClick = (activeTab: any) => devTools.update(INDEX_KEY, { activeTab });
|
||||
const onTabClick = (activeTab: any) =>
|
||||
devTools.update(INDEX_KEY, { activeTab });
|
||||
const onFilterChange = ({ target: { value } }: any) =>
|
||||
devTools.update(INDEX_KEY, { filter: value });
|
||||
|
||||
|
|
@ -136,34 +146,35 @@ function MobileConsolePanel() {
|
|||
<BottomBlock.Header>
|
||||
<div className="flex items-center">
|
||||
<span className="font-semibold color-gray-medium mr-4">Console</span>
|
||||
<Tabs tabs={TABS} active={activeTab} onClick={onTabClick} border={false} />
|
||||
<Tabs
|
||||
tabs={TABS}
|
||||
active={activeTab}
|
||||
onClick={onTabClick}
|
||||
border={false}
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
className="input-small h-8"
|
||||
className="rounded-lg"
|
||||
placeholder="Filter by keyword"
|
||||
icon="search"
|
||||
name="filter"
|
||||
height={28}
|
||||
onChange={onFilterChange}
|
||||
value={filter}
|
||||
size="small"
|
||||
prefix={<SearchOutlined className="text-neutral-400" />}
|
||||
/>
|
||||
</BottomBlock.Header>
|
||||
<BottomBlock.Content className="overflow-y-auto">
|
||||
<NoContent
|
||||
title={
|
||||
<div className="capitalize flex items-center mt-16">
|
||||
<Icon name="info-circle" className="mr-2" size="18" />
|
||||
<div className="capitalize flex items-center mt-16 gap-2">
|
||||
<InfoCircleOutlined size={18} />
|
||||
No Data
|
||||
</div>
|
||||
}
|
||||
size="small"
|
||||
show={filteredList.length === 0}
|
||||
>
|
||||
<VList
|
||||
ref={_list}
|
||||
itemSize={25}
|
||||
count={filteredList.length || 1}
|
||||
>
|
||||
<VList ref={_list} itemSize={25} count={filteredList.length || 1}>
|
||||
{filteredList.map((log, index) => (
|
||||
<ConsoleRow
|
||||
key={log.time + index}
|
||||
|
|
@ -172,6 +183,7 @@ function MobileConsolePanel() {
|
|||
iconProps={getIconProps(log.level)}
|
||||
renderWithNL={renderWithNL}
|
||||
onClick={() => showDetails(log)}
|
||||
showSingleTab
|
||||
/>
|
||||
))}
|
||||
</VList>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import React, { useState } from 'react';
|
|||
import cn from 'classnames';
|
||||
import { Icon } from 'UI';
|
||||
import JumpButton from 'Shared/DevTools/JumpButton';
|
||||
import { Tag } from 'antd';
|
||||
import TabTag from "../TabTag";
|
||||
|
||||
interface Props {
|
||||
log: any;
|
||||
|
|
@ -10,6 +12,8 @@ interface Props {
|
|||
renderWithNL?: any;
|
||||
style?: any;
|
||||
onClick?: () => void;
|
||||
getTabNum?: (tab: string) => number;
|
||||
showSingleTab: boolean;
|
||||
}
|
||||
function ConsoleRow(props: Props) {
|
||||
const { log, iconProps, jump, renderWithNL, style } = props;
|
||||
|
|
@ -41,11 +45,13 @@ function ConsoleRow(props: Props) {
|
|||
|
||||
const titleLine = lines[0];
|
||||
const restLines = lines.slice(1);
|
||||
const logSource = props.showSingleTab ? -1 : props.getTabNum?.(log.tabId);
|
||||
const logTabId = log.tabId
|
||||
return (
|
||||
<div
|
||||
style={style}
|
||||
className={cn(
|
||||
'border-b flex items-start py-1 px-4 pe-8 overflow-hidden group relative',
|
||||
'border-b border-neutral-950/5 flex items-start gap-2 py-1 px-4 pe-8 overflow-hidden group relative',
|
||||
{
|
||||
info: !log.isYellow && !log.isRed,
|
||||
warn: log.isYellow,
|
||||
|
|
@ -55,11 +61,10 @@ function ConsoleRow(props: Props) {
|
|||
)}
|
||||
onClick={clickable ? () => (!!log.errorId ? props.onClick?.() : toggleExpand()) : undefined}
|
||||
>
|
||||
<div className="mr-2">
|
||||
<Icon size="14" {...iconProps} />
|
||||
</div>
|
||||
{logSource !== -1 && <TabTag logSource={logSource} logTabId={logTabId} />}
|
||||
<Icon size="14" {...iconProps} className='mt-0.5' />
|
||||
<div key={log.key} data-scroll-item={log.isRed}>
|
||||
<div className="flex items-start text-sm ">
|
||||
<div className="flex items-start text-sm">
|
||||
<div className={cn('flex items-start', { 'cursor-pointer underline decoration-dotted decoration-gray-400': !!log.errorId })}>
|
||||
{canExpand && (
|
||||
<Icon name={expanded ? 'caret-down-fill' : 'caret-right-fill'} className="mr-2" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import React from 'react';
|
||||
import { Icon, Tooltip } from 'UI';
|
||||
import { shortDurationFromMs } from "App/date";
|
||||
import { Tooltip } from 'UI';
|
||||
import { CaretRightOutlined } from '@ant-design/icons';
|
||||
import { Button } from 'antd';
|
||||
import { shortDurationFromMs } from 'App/date';
|
||||
|
||||
interface Props {
|
||||
onClick: any;
|
||||
|
|
@ -12,19 +14,24 @@ function JumpButton(props: Props) {
|
|||
return (
|
||||
<div className="absolute right-2 top-0 bottom-0 my-auto flex items-center">
|
||||
<Tooltip title={tooltip} disabled={!tooltip}>
|
||||
<div
|
||||
className="border cursor-pointer hidden group-hover:flex rounded bg-white text-xs items-center px-2 py-1 color-teal hover:shadow h-6"
|
||||
<Button
|
||||
type="default"
|
||||
size="small"
|
||||
className="hidden group-hover:flex rounded-lg text-xs p-1 py-0 gap-0 h-6"
|
||||
iconPosition="end"
|
||||
onClick={(e: any) => {
|
||||
e.stopPropagation();
|
||||
props.onClick();
|
||||
}}
|
||||
icon={<CaretRightOutlined />}
|
||||
>
|
||||
<Icon name="caret-right-fill" size="12" color="teal" />
|
||||
<span>JUMP</span>
|
||||
</div>
|
||||
{props.time ? <div className={'block group-hover:hidden mr-2'}>
|
||||
{shortDurationFromMs(props.time)}
|
||||
</div> : null}
|
||||
JUMP
|
||||
</Button>
|
||||
{props.time ? (
|
||||
<div className={'block group-hover:hidden mr-2 text-sm'}>
|
||||
{shortDurationFromMs(props.time)}
|
||||
</div>
|
||||
) : null}
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { ResourceType, Timed } from 'Player';
|
||||
import MobilePlayer from 'Player/mobile/IOSPlayer';
|
||||
import WebPlayer from 'Player/web/WebPlayer';
|
||||
import { Duration } from 'luxon';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
|
|
@ -13,17 +12,19 @@ import {
|
|||
import { formatMs } from 'App/date';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { formatBytes } from 'App/utils';
|
||||
import { Icon, Input, NoContent, Tabs, Toggler, Tooltip } from 'UI';
|
||||
import { Icon, NoContent, Tabs } from 'UI';
|
||||
import { Tooltip, Input, Switch, Form } from 'antd';
|
||||
import { SearchOutlined, InfoCircleOutlined } from '@ant-design/icons';
|
||||
|
||||
import FetchDetailsModal from 'Shared/FetchDetailsModal';
|
||||
import { WsChannel } from "App/player/web/messages";
|
||||
import { WsChannel } from 'App/player/web/messages';
|
||||
|
||||
import BottomBlock from '../BottomBlock';
|
||||
import InfoLine from '../BottomBlock/InfoLine';
|
||||
import TabSelector from '../TabSelector';
|
||||
import TimeTable from '../TimeTable';
|
||||
import useAutoscroll, { getLastItemTime } from '../useAutoscroll';
|
||||
import { useRegExListFilterMemo, useTabListFilterMemo } from '../useListFilter';
|
||||
import WSModal from './WSModal';
|
||||
import WSPanel from './WSPanel';
|
||||
|
||||
const INDEX_KEY = 'network';
|
||||
|
|
@ -57,12 +58,6 @@ export const NETWORK_TABS = TAP_KEYS.map((tab) => ({
|
|||
const DOM_LOADED_TIME_COLOR = 'teal';
|
||||
const LOAD_TIME_COLOR = 'red';
|
||||
|
||||
function compare(a: any, b: any, key: string) {
|
||||
if (a[key] > b[key]) return 1;
|
||||
if (a[key] < b[key]) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function renderType(r: any) {
|
||||
return (
|
||||
<Tooltip style={{ width: '100%' }} title={<div>{r.type}</div>}>
|
||||
|
|
@ -79,14 +74,6 @@ export function renderName(r: any) {
|
|||
);
|
||||
}
|
||||
|
||||
export function renderStart(r: any) {
|
||||
return (
|
||||
<div className="flex justify-between items-center grow-0 w-full">
|
||||
<span>{Duration.fromMillis(r.time).toFormat('mm:ss.SSS')}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderSize(r: any) {
|
||||
if (r.responseBodySize) return formatBytes(r.responseBodySize);
|
||||
let triggerText;
|
||||
|
|
@ -125,13 +112,10 @@ export function renderDuration(r: any) {
|
|||
if (!r.isRed && !r.isYellow) return text;
|
||||
|
||||
let tooltipText;
|
||||
let className = 'w-full h-full flex items-center ';
|
||||
if (r.isYellow) {
|
||||
tooltipText = 'Slower than average';
|
||||
className += 'warn color-orange';
|
||||
} else {
|
||||
tooltipText = 'Much slower than average';
|
||||
className += 'error color-red';
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -151,7 +135,7 @@ function renderStatus({
|
|||
error?: string;
|
||||
}) {
|
||||
const displayedStatus = error ? (
|
||||
<Tooltip delay={0} title={error}>
|
||||
<Tooltip title={error}>
|
||||
<div
|
||||
style={{ width: 90 }}
|
||||
className={'overflow-hidden overflow-ellipsis'}
|
||||
|
|
@ -165,7 +149,7 @@ function renderStatus({
|
|||
return (
|
||||
<>
|
||||
{cached ? (
|
||||
<Tooltip title={'Served from cache'}>
|
||||
<Tooltip title={'Served from cache'} placement="top">
|
||||
<div className="flex items-center">
|
||||
<span className="mr-1">{displayedStatus}</span>
|
||||
<Icon name="wifi" size={16} />
|
||||
|
|
@ -178,13 +162,10 @@ function renderStatus({
|
|||
);
|
||||
}
|
||||
|
||||
function NetworkPanelCont({
|
||||
panelHeight,
|
||||
}: {
|
||||
panelHeight: number;
|
||||
}) {
|
||||
function NetworkPanelCont({ panelHeight }: { panelHeight: number }) {
|
||||
const { player, store } = React.useContext(PlayerContext);
|
||||
const { sessionStore } = useStore();
|
||||
const { sessionStore, uiPlayerStore } = useStore();
|
||||
|
||||
const startedAt = sessionStore.current.startedAt;
|
||||
const {
|
||||
domContentLoadedTime,
|
||||
|
|
@ -192,7 +173,12 @@ function NetworkPanelCont({
|
|||
domBuildingTime,
|
||||
tabStates,
|
||||
currentTab,
|
||||
tabNames,
|
||||
} = store.get();
|
||||
const tabsArr = Object.keys(tabStates);
|
||||
const tabValues = Object.values(tabStates);
|
||||
const dataSource = uiPlayerStore.dataSource;
|
||||
const showSingleTab = dataSource === 'current';
|
||||
const {
|
||||
fetchList = [],
|
||||
resourceList = [],
|
||||
|
|
@ -200,8 +186,34 @@ function NetworkPanelCont({
|
|||
resourceListNow = [],
|
||||
websocketList = [],
|
||||
websocketListNow = [],
|
||||
} = tabStates[currentTab];
|
||||
|
||||
} = React.useMemo(() => {
|
||||
if (showSingleTab) {
|
||||
return tabStates[currentTab] ?? {};
|
||||
} else {
|
||||
const fetchList = tabValues.flatMap((tab) => tab.fetchList);
|
||||
const resourceList = tabValues.flatMap((tab) => tab.resourceList);
|
||||
const fetchListNow = tabValues
|
||||
.flatMap((tab) => tab.fetchListNow)
|
||||
.filter(Boolean);
|
||||
const resourceListNow = tabValues
|
||||
.flatMap((tab) => tab.resourceListNow)
|
||||
.filter(Boolean);
|
||||
const websocketList = tabValues.flatMap((tab) => tab.websocketList);
|
||||
const websocketListNow = tabValues
|
||||
.flatMap((tab) => tab.websocketListNow)
|
||||
.filter(Boolean);
|
||||
return {
|
||||
fetchList,
|
||||
resourceList,
|
||||
fetchListNow,
|
||||
resourceListNow,
|
||||
websocketList,
|
||||
websocketListNow,
|
||||
};
|
||||
}
|
||||
}, [currentTab, tabStates, dataSource, tabValues]);
|
||||
const getTabNum = (tab: string) => tabsArr.findIndex((t) => t === tab) + 1;
|
||||
const getTabName = (tabId: string) => tabNames[tabId]
|
||||
return (
|
||||
<NetworkPanelComp
|
||||
loadTime={loadTime}
|
||||
|
|
@ -216,15 +228,14 @@ function NetworkPanelCont({
|
|||
startedAt={startedAt}
|
||||
websocketList={websocketList as WSMessage[]}
|
||||
websocketListNow={websocketListNow as WSMessage[]}
|
||||
getTabNum={getTabNum}
|
||||
getTabName={getTabName}
|
||||
showSingleTab={showSingleTab}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function MobileNetworkPanelCont({
|
||||
panelHeight,
|
||||
}: {
|
||||
panelHeight: number;
|
||||
}) {
|
||||
function MobileNetworkPanelCont({ panelHeight }: { panelHeight: number }) {
|
||||
const { player, store } = React.useContext(MobilePlayerContext);
|
||||
const { uiPlayerStore, sessionStore } = useStore();
|
||||
const startedAt = sessionStore.current.startedAt;
|
||||
|
|
@ -301,6 +312,9 @@ interface Props {
|
|||
onClose?: () => void;
|
||||
activeOutsideIndex?: number;
|
||||
isSpot?: boolean;
|
||||
getTabNum?: (tab: string) => number;
|
||||
getTabName?: (tabId: string) => string;
|
||||
showSingleTab?: boolean;
|
||||
}
|
||||
|
||||
export const NetworkPanelComp = observer(
|
||||
|
|
@ -323,8 +337,13 @@ export const NetworkPanelComp = observer(
|
|||
onClose,
|
||||
activeOutsideIndex,
|
||||
isSpot,
|
||||
getTabNum,
|
||||
showSingleTab,
|
||||
getTabName,
|
||||
}: Props) => {
|
||||
const [selectedWsChannel, setSelectedWsChannel] = React.useState<WsChannel[] | null>(null)
|
||||
const [selectedWsChannel, setSelectedWsChannel] = React.useState<
|
||||
WsChannel[] | null
|
||||
>(null);
|
||||
const { showModal } = useModal();
|
||||
const [showOnlyErrors, setShowOnlyErrors] = useState(false);
|
||||
|
||||
|
|
@ -480,10 +499,10 @@ export const NetworkPanelComp = observer(
|
|||
const showDetailsModal = (item: any) => {
|
||||
if (item.type === 'websocket') {
|
||||
const socketMsgList = websocketList.filter(
|
||||
(ws) => ws.channelName === item.channelName
|
||||
);
|
||||
(ws) => ws.channelName === item.channelName
|
||||
);
|
||||
|
||||
return setSelectedWsChannel(socketMsgList)
|
||||
return setSelectedWsChannel(socketMsgList);
|
||||
}
|
||||
setIsDetailsModalActive(true);
|
||||
showModal(
|
||||
|
|
@ -507,6 +526,61 @@ export const NetworkPanelComp = observer(
|
|||
stopAutoscroll();
|
||||
};
|
||||
|
||||
const tableCols = React.useMemo(() => {
|
||||
const cols: any[] = [
|
||||
{
|
||||
label: 'Status',
|
||||
dataKey: 'status',
|
||||
width: 90,
|
||||
render: renderStatus,
|
||||
},
|
||||
{
|
||||
label: 'Type',
|
||||
dataKey: 'type',
|
||||
width: 90,
|
||||
render: renderType,
|
||||
},
|
||||
{
|
||||
label: 'Method',
|
||||
width: 80,
|
||||
dataKey: 'method',
|
||||
},
|
||||
{
|
||||
label: 'Name',
|
||||
width: 240,
|
||||
dataKey: 'name',
|
||||
render: renderName,
|
||||
},
|
||||
{
|
||||
label: 'Size',
|
||||
width: 80,
|
||||
dataKey: 'decodedBodySize',
|
||||
render: renderSize,
|
||||
hidden: activeTab === XHR,
|
||||
},
|
||||
{
|
||||
label: 'Duration',
|
||||
width: 80,
|
||||
dataKey: 'duration',
|
||||
render: renderDuration,
|
||||
},
|
||||
];
|
||||
if (!showSingleTab && !isSpot) {
|
||||
cols.unshift({
|
||||
label: 'Source',
|
||||
width: 64,
|
||||
render: (r: Record<string, any>) => (
|
||||
<Tooltip title={`${getTabName?.(r.tabId) ?? `Tab ${getTabNum?.(r.tabId) ?? 0}`}`} placement="left">
|
||||
<div className="bg-gray-light rounded-full min-w-5 min-h-5 w-5 h-5 flex items-center justify-center text-xs cursor-default">
|
||||
{getTabNum?.(r.tabId) ?? 0}
|
||||
</div>
|
||||
</Tooltip>
|
||||
),
|
||||
});
|
||||
}
|
||||
return cols;
|
||||
}, [showSingleTab]);
|
||||
|
||||
return (
|
||||
<BottomBlock
|
||||
style={{ height: '100%' }}
|
||||
|
|
@ -529,26 +603,39 @@ export const NetworkPanelComp = observer(
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
<Input
|
||||
className="input-small"
|
||||
placeholder="Filter by name, type, method or value"
|
||||
icon="search"
|
||||
name="filter"
|
||||
onChange={onFilterChange}
|
||||
height={28}
|
||||
width={280}
|
||||
value={filter}
|
||||
/>
|
||||
<div className={'flex items-center gap-2'}>
|
||||
{!isMobile && !isSpot ? <TabSelector /> : null}
|
||||
<Input
|
||||
className="rounded-lg"
|
||||
placeholder="Filter by name, type, method or value"
|
||||
name="filter"
|
||||
onChange={onFilterChange}
|
||||
width={280}
|
||||
value={filter}
|
||||
size="small"
|
||||
prefix={<SearchOutlined className="text-neutral-400" />}
|
||||
/>
|
||||
</div>
|
||||
</BottomBlock.Header>
|
||||
<BottomBlock.Content>
|
||||
<div className="flex items-center justify-between px-4 border-b bg-teal/5 h-8">
|
||||
<div>
|
||||
<Toggler
|
||||
checked={showOnlyErrors}
|
||||
name="show-errors-only"
|
||||
onChange={() => setShowOnlyErrors(!showOnlyErrors)}
|
||||
label="4xx-5xx Only"
|
||||
/>
|
||||
<Form.Item name="show-errors-only" className="mb-0">
|
||||
<label
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
<Switch
|
||||
checked={showOnlyErrors}
|
||||
onChange={() => setShowOnlyErrors(!showOnlyErrors)}
|
||||
size="small"
|
||||
/>
|
||||
<span className="text-sm ms-2">4xx-5xx Only</span>
|
||||
</label>
|
||||
</Form.Item>
|
||||
</div>
|
||||
<InfoLine>
|
||||
<InfoLine.Point
|
||||
|
|
@ -588,8 +675,8 @@ export const NetworkPanelComp = observer(
|
|||
</div>
|
||||
<NoContent
|
||||
title={
|
||||
<div className="capitalize flex items-center">
|
||||
<Icon name="info-circle" className="mr-2" size="18" />
|
||||
<div className="capitalize flex items-center gap-2">
|
||||
<InfoCircleOutlined size={18} />
|
||||
No Data
|
||||
</div>
|
||||
}
|
||||
|
|
@ -613,52 +700,13 @@ export const NetworkPanelComp = observer(
|
|||
}}
|
||||
activeIndex={activeIndex}
|
||||
>
|
||||
{[
|
||||
// {
|
||||
// label: 'Start',
|
||||
// width: 120,
|
||||
// render: renderStart,
|
||||
// },
|
||||
{
|
||||
label: 'Status',
|
||||
dataKey: 'status',
|
||||
width: 90,
|
||||
render: renderStatus,
|
||||
},
|
||||
{
|
||||
label: 'Type',
|
||||
dataKey: 'type',
|
||||
width: 90,
|
||||
render: renderType,
|
||||
},
|
||||
{
|
||||
label: 'Method',
|
||||
width: 80,
|
||||
dataKey: 'method',
|
||||
},
|
||||
{
|
||||
label: 'Name',
|
||||
width: 240,
|
||||
dataKey: 'name',
|
||||
render: renderName,
|
||||
},
|
||||
{
|
||||
label: 'Size',
|
||||
width: 80,
|
||||
dataKey: 'decodedBodySize',
|
||||
render: renderSize,
|
||||
hidden: activeTab === XHR,
|
||||
},
|
||||
{
|
||||
label: 'Duration',
|
||||
width: 80,
|
||||
dataKey: 'duration',
|
||||
render: renderDuration,
|
||||
},
|
||||
]}
|
||||
{tableCols}
|
||||
</TimeTable>
|
||||
{selectedWsChannel ? (
|
||||
<WSPanel socketMsgList={selectedWsChannel} onClose={() => setSelectedWsChannel(null)} />
|
||||
<WSPanel
|
||||
socketMsgList={selectedWsChannel}
|
||||
onClose={() => setSelectedWsChannel(null)}
|
||||
/>
|
||||
) : null}
|
||||
</NoContent>
|
||||
</BottomBlock.Content>
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ const lineLength = 40;
|
|||
function WSPanel({ socketMsgList, onClose }: Props) {
|
||||
const [query, setQuery] = React.useState('');
|
||||
const [list, setList] = React.useState(socketMsgList);
|
||||
const [selectedRow, setSelectedRow] = React.useState<SocketMsg | null>(null);
|
||||
const [selectedRow, setSelectedRow] = React.useState<{ msg: SocketMsg, id: number } | null>(null);
|
||||
|
||||
const onQueryChange = (e) => {
|
||||
const onQueryChange = (e: any) => {
|
||||
setQuery(e.target.value);
|
||||
const newList = filterList(socketMsgList, e.target.value, [
|
||||
'data',
|
||||
|
|
@ -69,15 +69,16 @@ function WSPanel({ socketMsgList, onClose }: Props) {
|
|||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{list.map((msg) => (
|
||||
{list.map((msg, i) => (
|
||||
<Row
|
||||
msg={msg}
|
||||
key={msg.timestamp}
|
||||
onSelect={() => setSelectedRow(msg)}
|
||||
onSelect={() => setSelectedRow({ msg, id: i })}
|
||||
isSelected={selectedRow ? selectedRow.id === i : false}
|
||||
/>
|
||||
))}
|
||||
{selectedRow ? (
|
||||
<SelectedRow msg={selectedRow} onClose={() => setSelectedRow(null)} />
|
||||
<SelectedRow msg={selectedRow.msg} onClose={() => setSelectedRow(null)} />
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -127,7 +128,7 @@ function MsgDirection({ dir }: { dir: 'up' | 'down' }) {
|
|||
);
|
||||
}
|
||||
|
||||
function Row({ msg, onSelect }: { msg: SocketMsg; onSelect: () => void }) {
|
||||
function Row({ msg, onSelect, isSelected }: { msg: SocketMsg; isSelected: boolean; onSelect: () => void }) {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
|
|
@ -149,7 +150,7 @@ function Row({ msg, onSelect }: { msg: SocketMsg; onSelect: () => void }) {
|
|||
'rounded-full font-bold text-xl p-2 bg-white w-6 h-6 flex items-center justify-center'
|
||||
}
|
||||
>
|
||||
<span>{isOpen ? '-' : '+'}</span>
|
||||
<span>{isSelected ? '-' : '+'}</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
import { Timed } from 'Player';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { Tabs, Input, NoContent, Icon } from 'UI';
|
||||
import { PlayerContext, MobilePlayerContext } from 'App/components/Session/playerContext';
|
||||
import { Tabs, NoContent, Icon } from 'UI';
|
||||
import { Input } from 'antd';
|
||||
import { SearchOutlined, InfoCircleOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
PlayerContext,
|
||||
MobilePlayerContext,
|
||||
} from 'App/components/Session/playerContext';
|
||||
import BottomBlock from '../BottomBlock';
|
||||
import { useModal } from 'App/components/Modal';
|
||||
import { useStore } from 'App/mstore';
|
||||
|
|
@ -10,6 +15,7 @@ import { typeList } from 'Types/session/stackEvent';
|
|||
import StackEventRow from 'Shared/DevTools/StackEventRow';
|
||||
|
||||
import StackEventModal from '../StackEventModal';
|
||||
import { Segmented, Tooltip } from 'antd';
|
||||
import useAutoscroll, { getLastItemTime } from '../useAutoscroll';
|
||||
import { useRegExListFilterMemo, useTabListFilterMemo } from '../useListFilter';
|
||||
import { VList, VListHandle } from 'virtua';
|
||||
|
|
@ -24,198 +30,247 @@ const ALL = 'ALL';
|
|||
const TAB_KEYS = [ALL, ...typeList] as const;
|
||||
const TABS = TAB_KEYS.map((tab) => ({ text: tab, key: tab }));
|
||||
|
||||
type EventsList = Array<Timed & { name: string; source: string; key: string; payload?: string[] }>;
|
||||
type EventsList = Array<
|
||||
Timed & { name: string; source: string; key: string; payload?: string[] }
|
||||
>;
|
||||
|
||||
const WebStackEventPanelComp = observer(
|
||||
() => {
|
||||
const { uiPlayerStore } = useStore();
|
||||
const zoomEnabled = uiPlayerStore.timelineZoom.enabled;
|
||||
const zoomStartTs = uiPlayerStore.timelineZoom.startTs;
|
||||
const zoomEndTs = uiPlayerStore.timelineZoom.endTs;
|
||||
const { player, store } = React.useContext(PlayerContext);
|
||||
const jump = (t: number) => player.jump(t);
|
||||
const { currentTab, tabStates } = store.get();
|
||||
const WebStackEventPanelComp = observer(() => {
|
||||
const { uiPlayerStore } = useStore();
|
||||
const zoomEnabled = uiPlayerStore.timelineZoom.enabled;
|
||||
const zoomStartTs = uiPlayerStore.timelineZoom.startTs;
|
||||
const zoomEndTs = uiPlayerStore.timelineZoom.endTs;
|
||||
const { player, store } = React.useContext(PlayerContext);
|
||||
const jump = (t: number) => player.jump(t);
|
||||
const { currentTab, tabStates } = store.get();
|
||||
|
||||
const { stackList: list = [], stackListNow: listNow = [] } = tabStates[currentTab];
|
||||
const { stackList: list = [], stackListNow: listNow = [] } =
|
||||
tabStates[currentTab];
|
||||
|
||||
return (
|
||||
<EventsPanel
|
||||
list={list as EventsList}
|
||||
listNow={listNow as EventsList}
|
||||
jump={jump}
|
||||
zoomEnabled={zoomEnabled}
|
||||
zoomStartTs={zoomStartTs}
|
||||
zoomEndTs={zoomEndTs}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
return (
|
||||
<EventsPanel
|
||||
list={list as EventsList}
|
||||
listNow={listNow as EventsList}
|
||||
jump={jump}
|
||||
zoomEnabled={zoomEnabled}
|
||||
zoomStartTs={zoomStartTs}
|
||||
zoomEndTs={zoomEndTs}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const WebStackEventPanel = WebStackEventPanelComp;
|
||||
|
||||
const MobileStackEventPanelComp = observer(
|
||||
() => {
|
||||
const { uiPlayerStore } = useStore();
|
||||
const zoomEnabled = uiPlayerStore.timelineZoom.enabled;
|
||||
const zoomStartTs = uiPlayerStore.timelineZoom.startTs;
|
||||
const zoomEndTs = uiPlayerStore.timelineZoom.endTs;
|
||||
const { player, store } = React.useContext(MobilePlayerContext);
|
||||
const jump = (t: number) => player.jump(t);
|
||||
const { eventList: list = [], eventListNow: listNow = [] } = store.get();
|
||||
const MobileStackEventPanelComp = observer(() => {
|
||||
const { uiPlayerStore } = useStore();
|
||||
const zoomEnabled = uiPlayerStore.timelineZoom.enabled;
|
||||
const zoomStartTs = uiPlayerStore.timelineZoom.startTs;
|
||||
const zoomEndTs = uiPlayerStore.timelineZoom.endTs;
|
||||
const { player, store } = React.useContext(MobilePlayerContext);
|
||||
const jump = (t: number) => player.jump(t);
|
||||
const { eventList: list = [], eventListNow: listNow = [] } = store.get();
|
||||
|
||||
return (
|
||||
<EventsPanel
|
||||
list={list as EventsList}
|
||||
listNow={listNow as EventsList}
|
||||
jump={jump}
|
||||
zoomEnabled={zoomEnabled}
|
||||
zoomStartTs={zoomStartTs}
|
||||
zoomEndTs={zoomEndTs}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
return (
|
||||
<EventsPanel
|
||||
list={list as EventsList}
|
||||
listNow={listNow as EventsList}
|
||||
jump={jump}
|
||||
isMobile
|
||||
zoomEnabled={zoomEnabled}
|
||||
zoomStartTs={zoomStartTs}
|
||||
zoomEndTs={zoomEndTs}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const MobileStackEventPanel = MobileStackEventPanelComp;
|
||||
|
||||
const EventsPanel = observer(({
|
||||
list,
|
||||
listNow,
|
||||
jump,
|
||||
zoomEnabled,
|
||||
zoomStartTs,
|
||||
zoomEndTs,
|
||||
}: {
|
||||
list: EventsList;
|
||||
listNow: EventsList;
|
||||
jump: (t: number) => void;
|
||||
zoomEnabled: boolean;
|
||||
zoomStartTs: number;
|
||||
zoomEndTs: number;
|
||||
}) => {
|
||||
const {
|
||||
sessionStore: { devTools },
|
||||
} = useStore();
|
||||
const { showModal } = useModal();
|
||||
const [isDetailsModalActive, setIsDetailsModalActive] = useState(false); // TODO:embed that into useModal
|
||||
const filter = devTools[INDEX_KEY].filter;
|
||||
const activeTab = devTools[INDEX_KEY].activeTab;
|
||||
const activeIndex = devTools[INDEX_KEY].index;
|
||||
const EventsPanel = observer(
|
||||
({
|
||||
list,
|
||||
listNow,
|
||||
jump,
|
||||
zoomEnabled,
|
||||
zoomStartTs,
|
||||
zoomEndTs,
|
||||
isMobile,
|
||||
}: {
|
||||
list: EventsList;
|
||||
listNow: EventsList;
|
||||
jump: (t: number) => void;
|
||||
zoomEnabled: boolean;
|
||||
zoomStartTs: number;
|
||||
zoomEndTs: number;
|
||||
isMobile?: boolean;
|
||||
}) => {
|
||||
const {
|
||||
sessionStore: { devTools },
|
||||
} = useStore();
|
||||
const { showModal } = useModal();
|
||||
const [isDetailsModalActive, setIsDetailsModalActive] = useState(false); // TODO:embed that into useModal
|
||||
const filter = devTools[INDEX_KEY].filter;
|
||||
const activeTab = devTools[INDEX_KEY].activeTab;
|
||||
const activeIndex = devTools[INDEX_KEY].index;
|
||||
|
||||
const inZoomRangeList = list.filter(({ time }) =>
|
||||
zoomEnabled ? zoomStartTs <= time && time <= zoomEndTs : true
|
||||
);
|
||||
const inZoomRangeListNow = listNow.filter(({ time }) =>
|
||||
zoomEnabled ? zoomStartTs <= time && time <= zoomEndTs : true
|
||||
);
|
||||
const inZoomRangeList = list.filter(({ time }) =>
|
||||
zoomEnabled ? zoomStartTs <= time && time <= zoomEndTs : true
|
||||
);
|
||||
const inZoomRangeListNow = listNow.filter(({ time }) =>
|
||||
zoomEnabled ? zoomStartTs <= time && time <= zoomEndTs : true
|
||||
);
|
||||
|
||||
let filteredList = useRegExListFilterMemo(inZoomRangeList, (it) => {
|
||||
const searchBy = [it.name]
|
||||
if (it.payload) {
|
||||
const payload = Array.isArray(it.payload) ? it.payload.join(',') : JSON.stringify(it.payload);
|
||||
searchBy.push(payload);
|
||||
}
|
||||
return searchBy
|
||||
}, filter);
|
||||
filteredList = useTabListFilterMemo(filteredList, (it) => it.source, ALL, activeTab);
|
||||
|
||||
const onTabClick = (activeTab: (typeof TAB_KEYS)[number]) =>
|
||||
devTools.update(INDEX_KEY, { activeTab });
|
||||
const onFilterChange = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => devTools.update(INDEX_KEY, { filter: value });
|
||||
const tabs = useMemo(
|
||||
() => TABS.filter(({ key }) => key === ALL || inZoomRangeList.some(({ source }) => key === source)),
|
||||
[inZoomRangeList.length]
|
||||
);
|
||||
|
||||
const [timeoutStartAutoscroll, stopAutoscroll] = useAutoscroll(
|
||||
filteredList,
|
||||
getLastItemTime(inZoomRangeListNow),
|
||||
activeIndex,
|
||||
(index) => devTools.update(INDEX_KEY, { index })
|
||||
);
|
||||
const onMouseEnter = stopAutoscroll;
|
||||
const onMouseLeave = () => {
|
||||
if (isDetailsModalActive) {
|
||||
return;
|
||||
}
|
||||
timeoutStartAutoscroll();
|
||||
};
|
||||
|
||||
const showDetails = (item: any) => {
|
||||
setIsDetailsModalActive(true);
|
||||
showModal(<StackEventModal event={item} />, {
|
||||
right: true,
|
||||
width: 500,
|
||||
onClose: () => {
|
||||
setIsDetailsModalActive(false);
|
||||
timeoutStartAutoscroll();
|
||||
let filteredList = useRegExListFilterMemo(
|
||||
inZoomRangeList,
|
||||
(it) => {
|
||||
const searchBy = [it.name];
|
||||
if (it.payload) {
|
||||
const payload = Array.isArray(it.payload)
|
||||
? it.payload.join(',')
|
||||
: JSON.stringify(it.payload);
|
||||
searchBy.push(payload);
|
||||
}
|
||||
return searchBy;
|
||||
},
|
||||
});
|
||||
devTools.update(INDEX_KEY, { index: filteredList.indexOf(item) });
|
||||
stopAutoscroll();
|
||||
};
|
||||
filter
|
||||
);
|
||||
filteredList = useTabListFilterMemo(
|
||||
filteredList,
|
||||
(it) => it.source,
|
||||
ALL,
|
||||
activeTab
|
||||
);
|
||||
|
||||
const _list = React.useRef<VListHandle>(null);
|
||||
useEffect(() => {
|
||||
if (_list.current) {
|
||||
_list.current.scrollToIndex(activeIndex);
|
||||
}
|
||||
}, [activeIndex]);
|
||||
const onTabClick = (activeTab: (typeof TAB_KEYS)[number]) =>
|
||||
devTools.update(INDEX_KEY, { activeTab });
|
||||
const onFilterChange = ({
|
||||
target: { value },
|
||||
}: React.ChangeEvent<HTMLInputElement>) =>
|
||||
devTools.update(INDEX_KEY, { filter: value });
|
||||
const tabs = useMemo(
|
||||
() =>
|
||||
TABS.filter(
|
||||
({ key }) =>
|
||||
key === ALL || inZoomRangeList.some(({ source }) => key === source)
|
||||
),
|
||||
[inZoomRangeList.length]
|
||||
);
|
||||
|
||||
return (
|
||||
<BottomBlock style={{ height: '100%' }} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
|
||||
<BottomBlock.Header>
|
||||
<div className="flex items-center">
|
||||
<span className="font-semibold color-gray-medium mr-4">Stack Events</span>
|
||||
<Tabs
|
||||
renameTab={mapNames}
|
||||
tabs={tabs}
|
||||
active={activeTab}
|
||||
onClick={onTabClick}
|
||||
border={false}
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
className="input-small h-8"
|
||||
placeholder="Filter by keyword"
|
||||
icon="search"
|
||||
name="filter"
|
||||
height={28}
|
||||
onChange={onFilterChange}
|
||||
value={filter}
|
||||
/>
|
||||
</BottomBlock.Header>
|
||||
<BottomBlock.Content className="overflow-y-auto">
|
||||
<NoContent
|
||||
title={
|
||||
<div className="capitalize flex items-center mt-16">
|
||||
<Icon name="info-circle" className="mr-2" size="18" />
|
||||
No Data
|
||||
</div>
|
||||
}
|
||||
size="small"
|
||||
show={filteredList.length === 0}
|
||||
>
|
||||
<VList
|
||||
ref={_list}
|
||||
count={filteredList.length || 1}
|
||||
>
|
||||
{filteredList.map((item, index) => (
|
||||
<StackEventRow
|
||||
isActive={activeIndex === index}
|
||||
key={item.key}
|
||||
event={item}
|
||||
onJump={() => {
|
||||
stopAutoscroll();
|
||||
devTools.update(INDEX_KEY, { index: filteredList.indexOf(item) });
|
||||
jump(item.time);
|
||||
}}
|
||||
onClick={() => showDetails(item)}
|
||||
const [timeoutStartAutoscroll, stopAutoscroll] = useAutoscroll(
|
||||
filteredList,
|
||||
getLastItemTime(inZoomRangeListNow),
|
||||
activeIndex,
|
||||
(index) => devTools.update(INDEX_KEY, { index })
|
||||
);
|
||||
const onMouseEnter = stopAutoscroll;
|
||||
const onMouseLeave = () => {
|
||||
if (isDetailsModalActive) {
|
||||
return;
|
||||
}
|
||||
timeoutStartAutoscroll();
|
||||
};
|
||||
|
||||
const showDetails = (item: any) => {
|
||||
setIsDetailsModalActive(true);
|
||||
showModal(<StackEventModal event={item} />, {
|
||||
right: true,
|
||||
width: 500,
|
||||
onClose: () => {
|
||||
setIsDetailsModalActive(false);
|
||||
timeoutStartAutoscroll();
|
||||
},
|
||||
});
|
||||
devTools.update(INDEX_KEY, { index: filteredList.indexOf(item) });
|
||||
stopAutoscroll();
|
||||
};
|
||||
|
||||
const _list = React.useRef<VListHandle>(null);
|
||||
useEffect(() => {
|
||||
if (_list.current) {
|
||||
_list.current.scrollToIndex(activeIndex);
|
||||
}
|
||||
}, [activeIndex]);
|
||||
|
||||
return (
|
||||
<BottomBlock
|
||||
style={{ height: '100%' }}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
<BottomBlock.Header>
|
||||
<div className="flex items-center">
|
||||
<span className="font-semibold color-gray-medium mr-4">
|
||||
Stack Events
|
||||
</span>
|
||||
<Tabs
|
||||
renameTab={mapNames}
|
||||
tabs={tabs}
|
||||
active={activeTab}
|
||||
onClick={onTabClick}
|
||||
border={false}
|
||||
/>
|
||||
</div>
|
||||
<div className={'flex items-center gap-2'}>
|
||||
{isMobile ? null : (
|
||||
<Segmented
|
||||
options={[
|
||||
{ label: 'All Tabs', value: 'all' },
|
||||
{
|
||||
label: (
|
||||
<Tooltip title="Stack Events overview is available only for all tabs combined.">
|
||||
<span>Current Tab</span>
|
||||
</Tooltip>
|
||||
),
|
||||
value: 'current',
|
||||
disabled: true,
|
||||
},
|
||||
]}
|
||||
defaultValue="all"
|
||||
size="small"
|
||||
className="rounded-full font-medium"
|
||||
/>
|
||||
))}
|
||||
</VList>
|
||||
</NoContent>
|
||||
</BottomBlock.Content>
|
||||
</BottomBlock>
|
||||
);
|
||||
});
|
||||
)}
|
||||
<Input
|
||||
className="rounded-lg"
|
||||
placeholder="Filter by keyword"
|
||||
name="filter"
|
||||
height={28}
|
||||
onChange={onFilterChange}
|
||||
value={filter}
|
||||
size="small"
|
||||
prefix={<SearchOutlined className="text-neutral-400" />}
|
||||
/>
|
||||
</div>
|
||||
</BottomBlock.Header>
|
||||
<BottomBlock.Content className="overflow-y-auto">
|
||||
<NoContent
|
||||
title={
|
||||
<div className="capitalize flex items-center mt-16 gap-2">
|
||||
<InfoCircleOutlined size={18} />
|
||||
No Data
|
||||
</div>
|
||||
}
|
||||
size="small"
|
||||
show={filteredList.length === 0}
|
||||
>
|
||||
<VList ref={_list} count={filteredList.length || 1}>
|
||||
{filteredList.map((item, index) => (
|
||||
<StackEventRow
|
||||
isActive={activeIndex === index}
|
||||
key={item.key}
|
||||
event={item}
|
||||
onJump={() => {
|
||||
stopAutoscroll();
|
||||
devTools.update(INDEX_KEY, {
|
||||
index: filteredList.indexOf(item),
|
||||
});
|
||||
jump(item.time);
|
||||
}}
|
||||
onClick={() => showDetails(item)}
|
||||
/>
|
||||
))}
|
||||
</VList>
|
||||
</NoContent>
|
||||
</BottomBlock.Content>
|
||||
</BottomBlock>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
|||
22
frontend/app/components/shared/DevTools/TabSelector.tsx
Normal file
22
frontend/app/components/shared/DevTools/TabSelector.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import React from 'react'
|
||||
import { Segmented } from 'antd'
|
||||
import { useStore } from 'App/mstore';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
||||
function TabSelector() {
|
||||
const { uiPlayerStore } = useStore();
|
||||
const currentValue = uiPlayerStore.dataSource;
|
||||
const options = [
|
||||
{ label: 'All Tabs', value: 'all' },
|
||||
{ label: 'Current Tab', value: 'current' }
|
||||
]
|
||||
|
||||
const onChange = (value: 'all' | 'current') => {
|
||||
uiPlayerStore.changeDataSource(value)
|
||||
}
|
||||
return (
|
||||
<Segmented options={options} value={currentValue} onChange={onChange} className='font-medium rounded-lg' size='small' />
|
||||
)
|
||||
}
|
||||
|
||||
export default observer(TabSelector)
|
||||
23
frontend/app/components/shared/DevTools/TabTag.tsx
Normal file
23
frontend/app/components/shared/DevTools/TabTag.tsx
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import React from 'react';
|
||||
import { Tooltip } from 'antd';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { PlayerContext } from 'Components/Session/playerContext';
|
||||
|
||||
function TabTag({ logSource, logTabId }: { logSource: number; logTabId: string }) {
|
||||
const { store } = React.useContext(PlayerContext);
|
||||
const { tabNames } = store.get();
|
||||
|
||||
return (
|
||||
<Tooltip title={`${tabNames[logTabId] ?? `Tab ${logSource}`}`} placement="left">
|
||||
<div
|
||||
className={
|
||||
'bg-gray-light rounded-full min-w-5 min-h-5 w-5 h-5 flex items-center justify-center text-xs cursor-default'
|
||||
}
|
||||
>
|
||||
{logSource}
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(TabTag);
|
||||
|
|
@ -199,7 +199,7 @@ export default class TimeTable extends React.PureComponent<Props, State> {
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'dev-row border-b border-color-gray-light-shade group items-center',
|
||||
'dev-row border-b border-neutral-950/5 group items-center text-sm',
|
||||
stl.row,
|
||||
{
|
||||
[stl.hoverable]: hoverable,
|
||||
|
|
@ -215,7 +215,7 @@ export default class TimeTable extends React.PureComponent<Props, State> {
|
|||
{columns
|
||||
.filter((i: any) => !i.hidden)
|
||||
.map(({ dataKey, render, width, label }) => (
|
||||
<div key={parseInt(label.replace(' ', '')+dataKey, 36)} className={cn(stl.cell, 'overflow-ellipsis overflow-hidden')} style={{ width: `${width}px` }}>
|
||||
<div key={parseInt(label.replace(' ', '')+dataKey, 36)} className={cn(stl.cell, 'overflow-ellipsis overflow-hidden !py-0.5')} style={{ width: `${width}px` }}>
|
||||
{render
|
||||
? render(row)
|
||||
: row[dataKey || ''] || <i className="color-gray-light">{'empty'}</i>}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,11 @@
|
|||
import React from 'react';
|
||||
// import Select from 'Shared/Select';
|
||||
import { Dropdown, MenuProps, Select, Space } from 'antd';
|
||||
import { DownOutlined, SmileOutlined } from '@ant-design/icons';
|
||||
import { MenuProps, Select } from 'antd';
|
||||
|
||||
interface Props {
|
||||
payload: any;
|
||||
}
|
||||
|
||||
function NodeDropdown(props: Props) {
|
||||
const items: MenuProps['items'] = [
|
||||
{
|
||||
key: '1',
|
||||
label: (
|
||||
<a target='_blank' rel='noopener noreferrer' href='https://www.antgroup.com'>
|
||||
1st menu item
|
||||
</a>
|
||||
)
|
||||
}
|
||||
];
|
||||
return (
|
||||
<Select style={{ width: 120 }} placeholder='Slect Event' dropdownStyle={{
|
||||
border: 'none'
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ function LiveSessionList() {
|
|||
const totalLiveSessions = sessionStore.totalLiveSessions;
|
||||
const loading = sessionStore.loadingLiveSessions;
|
||||
const { currentPage } = searchStoreLive;
|
||||
const metaList = customFieldStore.list
|
||||
const metaList = customFieldStore.list;
|
||||
const metaListLoading = customFieldStore.isLoading;
|
||||
|
||||
let timeoutId: any;
|
||||
|
|
@ -32,7 +32,7 @@ function LiveSessionList() {
|
|||
const hasUserFilter = filters.map((i: any) => i.key).includes(KEYS.USERID);
|
||||
const sortOptions = [{ label: 'Start Time', value: 'timestamp' }].concat(
|
||||
metaList
|
||||
.map(({ key} : any) => ({
|
||||
.map(({ key }: any) => ({
|
||||
label: capitalize(key),
|
||||
value: key
|
||||
}))
|
||||
|
|
@ -40,21 +40,33 @@ function LiveSessionList() {
|
|||
|
||||
useEffect(() => {
|
||||
if (metaListLoading) return;
|
||||
|
||||
const _filter = { ...filter };
|
||||
let shouldUpdate = false;
|
||||
|
||||
// Set default sort if not already set
|
||||
if (sortOptions[1] && !filter.sort) {
|
||||
_filter.sort = sortOptions[1].value;
|
||||
shouldUpdate = true;
|
||||
}
|
||||
searchStoreLive.edit(_filter);
|
||||
|
||||
// Only update filters if there's a change
|
||||
if (shouldUpdate) {
|
||||
searchStoreLive.edit(_filter);
|
||||
}
|
||||
|
||||
// Start auto-refresh timeout
|
||||
timeout();
|
||||
|
||||
// Cleanup on component unmount or re-run
|
||||
return () => {
|
||||
clearTimeout(timeoutId);
|
||||
};
|
||||
}, [metaListLoading]);
|
||||
}, [metaListLoading, filter.sort]); // Add necessary dependencies
|
||||
|
||||
const refetch = () => {
|
||||
searchStoreLive.edit({ ...filter })
|
||||
void searchStoreLive.fetchSessions();
|
||||
}
|
||||
};
|
||||
|
||||
const onUserClick = (userId: string, userAnonymousId: string) => {
|
||||
if (userId) {
|
||||
|
|
@ -66,7 +78,6 @@ function LiveSessionList() {
|
|||
|
||||
const onSortChange = ({ value }: any) => {
|
||||
searchStoreLive.edit({ sort: value.value });
|
||||
void searchStoreLive.fetchSessions();
|
||||
};
|
||||
|
||||
const timeout = () => {
|
||||
|
|
@ -102,8 +113,7 @@ function LiveSessionList() {
|
|||
<div className="mx-2" />
|
||||
<SortOrderButton
|
||||
onChange={(state: any) => {
|
||||
searchStoreLive.edit({ order: state })
|
||||
void searchStoreLive.fetchSessions();
|
||||
searchStoreLive.edit({ order: state });
|
||||
}}
|
||||
sortOrder={filter.order}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ function LiveSessionSearch() {
|
|||
|
||||
const onUpdateFilter = (filterIndex: number, filter: any) => {
|
||||
searchStoreLive.updateFilter(filterIndex, filter);
|
||||
void searchStoreLive.fetchSessions();
|
||||
};
|
||||
|
||||
const onRemoveFilter = (filterIndex: number) => {
|
||||
|
|
@ -33,16 +32,12 @@ function LiveSessionSearch() {
|
|||
searchStoreLive.edit({
|
||||
filters: newFilters
|
||||
});
|
||||
|
||||
void searchStoreLive.fetchSessions();
|
||||
};
|
||||
|
||||
const onChangeEventsOrder = (e: any, { name, value }: any) => {
|
||||
searchStoreLive.edit({
|
||||
eventsOrder: value
|
||||
});
|
||||
|
||||
void searchStoreLive.fetchSessions();
|
||||
};
|
||||
|
||||
return (hasEvents || hasFilters) ? (
|
||||
|
|
|
|||
|
|
@ -11,50 +11,57 @@ import { useStore } from 'App/mstore';
|
|||
import { debounce } from 'App/utils';
|
||||
import useSessionSearchQueryHandler from 'App/hooks/useSessionSearchQueryHandler';
|
||||
|
||||
let debounceFetch: any = () => {
|
||||
};
|
||||
let debounceFetch: () => void;
|
||||
|
||||
function SessionSearch() {
|
||||
const { tagWatchStore, aiFiltersStore, searchStore, customFieldStore, projectsStore } = useStore();
|
||||
const appliedFilter = searchStore.instance;
|
||||
const metaLoading = customFieldStore.isLoading;
|
||||
const hasEvents = appliedFilter.filters.filter((i: any) => i.isEvent).length > 0;
|
||||
const hasFilters = appliedFilter.filters.filter((i: any) => !i.isEvent).length > 0;
|
||||
const hasEvents = appliedFilter.filters.some((i: any) => i.isEvent);
|
||||
const hasFilters = appliedFilter.filters.some((i: any) => !i.isEvent);
|
||||
const saveRequestPayloads = projectsStore.instance?.saveRequestPayloads ?? false;
|
||||
|
||||
useSessionSearchQueryHandler({
|
||||
appliedFilter,
|
||||
loading: metaLoading,
|
||||
onBeforeLoad: async () => {
|
||||
const tags = await tagWatchStore.getTags();
|
||||
if (tags) {
|
||||
addOptionsToFilter(
|
||||
FilterKey.TAGGED_ELEMENT,
|
||||
tags.map((tag) => ({
|
||||
label: tag.name,
|
||||
value: tag.tagId.toString()
|
||||
}))
|
||||
);
|
||||
searchStore.refreshFilterOptions();
|
||||
try {
|
||||
const tags = await tagWatchStore.getTags();
|
||||
if (tags) {
|
||||
addOptionsToFilter(
|
||||
FilterKey.TAGGED_ELEMENT,
|
||||
tags.map((tag) => ({
|
||||
label: tag.name,
|
||||
value: tag.tagId.toString()
|
||||
}))
|
||||
);
|
||||
searchStore.refreshFilterOptions();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error during onBeforeLoad:', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
debounceFetch = debounce(() => searchStore.fetchSessions(), 500);
|
||||
// void searchStore.fetchSessions(true)
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (searchStore.urlParsed) return;
|
||||
debounceFetch();
|
||||
}, [appliedFilter.filters]);
|
||||
|
||||
const onAddFilter = (filter: any) => {
|
||||
searchStore.addFilter(filter);
|
||||
|
||||
debounceFetch();
|
||||
};
|
||||
|
||||
const onUpdateFilter = (filterIndex: any, filter: any) => {
|
||||
searchStore.updateFilter(filterIndex, filter);
|
||||
|
||||
debounceFetch();
|
||||
};
|
||||
|
||||
const onFilterMove = (newFilters: any) => {
|
||||
|
|
@ -85,49 +92,47 @@ function SessionSearch() {
|
|||
};
|
||||
|
||||
const showPanel = hasEvents || hasFilters || aiFiltersStore.isLoading;
|
||||
return !metaLoading ? (
|
||||
<>
|
||||
{showPanel ? (
|
||||
<div className="border bg-white rounded-lg mt-4">
|
||||
<div className="p-5">
|
||||
{aiFiltersStore.isLoading ? (
|
||||
<div className={'font-semibold flex items-center gap-2 mb-2'}>
|
||||
<AnimatedSVG name={ICONS.LOADER} size={18} />
|
||||
<span>Translating your query into search steps...</span>
|
||||
</div>
|
||||
) : null}
|
||||
{hasEvents || hasFilters ? (
|
||||
<FilterList
|
||||
filter={appliedFilter}
|
||||
onUpdateFilter={onUpdateFilter}
|
||||
onRemoveFilter={onRemoveFilter}
|
||||
onChangeEventsOrder={onChangeEventsOrder}
|
||||
onFilterMove={onFilterMove}
|
||||
saveRequestPayloads={saveRequestPayloads}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{hasEvents || hasFilters ? (
|
||||
<div className="border-t px-5 py-1 flex items-center -mx-2">
|
||||
<div>
|
||||
<FilterSelection filter={undefined} onFilterClick={onAddFilter}>
|
||||
<Button variant="text-primary" className="mr-2" icon="plus">
|
||||
ADD STEP
|
||||
</Button>
|
||||
</FilterSelection>
|
||||
</div>
|
||||
<div className="ml-auto flex items-center">
|
||||
<SaveFilterButton />
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
if (metaLoading) return null;
|
||||
if (!showPanel) return null;
|
||||
|
||||
return (
|
||||
<div className="border bg-white rounded-lg mt-4">
|
||||
<div className="p-5">
|
||||
{aiFiltersStore.isLoading ? (
|
||||
<div className={'font-semibold flex items-center gap-2 mb-2'}>
|
||||
<AnimatedSVG name={ICONS.LOADER} size={18} />
|
||||
<span>Translating your query into search steps...</span>
|
||||
</div>
|
||||
) : null}
|
||||
{hasEvents || hasFilters ? (
|
||||
<FilterList
|
||||
filter={appliedFilter}
|
||||
onUpdateFilter={onUpdateFilter}
|
||||
onRemoveFilter={onRemoveFilter}
|
||||
onChangeEventsOrder={onChangeEventsOrder}
|
||||
onFilterMove={onFilterMove}
|
||||
saveRequestPayloads={saveRequestPayloads}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{hasEvents || hasFilters ? (
|
||||
<div className="border-t px-5 py-1 flex items-center -mx-2">
|
||||
<div>
|
||||
<FilterSelection filter={undefined} onFilterClick={onAddFilter}>
|
||||
<Button variant="text-primary" className="mr-2" icon="plus">
|
||||
ADD STEP
|
||||
</Button>
|
||||
</FilterSelection>
|
||||
</div>
|
||||
<div className="ml-auto flex items-center">
|
||||
<SaveFilterButton />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
) : null;
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(SessionSearch);
|
||||
|
|
|
|||
|
|
@ -3,16 +3,17 @@ import React from 'react';
|
|||
import { useStore } from 'App/mstore';
|
||||
|
||||
import LatestSessionsMessage from './components/LatestSessionsMessage';
|
||||
import NotesList from './components/Notes/NoteList';
|
||||
import SessionHeader from './components/SessionHeader';
|
||||
import SessionList from './components/SessionList';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import NoSessionsMessage from 'Shared/NoSessionsMessage/NoSessionsMessage';
|
||||
import MainSearchBar from 'Shared/MainSearchBar/MainSearchBar';
|
||||
import SessionSearch from 'Shared/SessionSearch/SessionSearch';
|
||||
|
||||
function SessionsTabOverview() {
|
||||
const [query, setQuery] = React.useState('');
|
||||
const { aiFiltersStore, searchStore } = useStore();
|
||||
const appliedFilter = searchStore.instance;
|
||||
const isNotesRoute = searchStore.activeTab.type === 'notes';
|
||||
|
||||
const handleKeyDown = (event: any) => {
|
||||
if (event.key === 'Enter') {
|
||||
|
|
@ -25,25 +26,27 @@ function SessionsTabOverview() {
|
|||
|
||||
const testingKey = localStorage.getItem('__mauricio_testing_access') === 'true';
|
||||
return (
|
||||
<div className="widget-wrapper">
|
||||
{testingKey ? (
|
||||
<Input
|
||||
value={query}
|
||||
onKeyDown={handleKeyDown}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
className={'mb-2'}
|
||||
placeholder={'ask session ai'}
|
||||
/>
|
||||
) : null}
|
||||
<SessionHeader />
|
||||
<div className="border-b" />
|
||||
<LatestSessionsMessage />
|
||||
{!isNotesRoute ? (
|
||||
<>
|
||||
<NoSessionsMessage />
|
||||
<MainSearchBar />
|
||||
<SessionSearch />
|
||||
<div className="my-4" />
|
||||
<div className="widget-wrapper">
|
||||
{testingKey ? (
|
||||
<Input
|
||||
value={query}
|
||||
onKeyDown={handleKeyDown}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
className={'mb-2'}
|
||||
placeholder={'ask session ai'}
|
||||
/>
|
||||
) : null}
|
||||
<SessionHeader />
|
||||
<div className="border-b" />
|
||||
<LatestSessionsMessage />
|
||||
<SessionList />
|
||||
) : (
|
||||
<NotesList />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,11 +7,16 @@ import { observer } from 'mobx-react-lite';
|
|||
function LatestSessionsMessage() {
|
||||
const { searchStore } = useStore();
|
||||
const count = searchStore.latestList.size;
|
||||
|
||||
const onShowNewSessions = () => {
|
||||
void searchStore.updateCurrentPage(1, true);
|
||||
};
|
||||
|
||||
return count > 0 ? (
|
||||
<div
|
||||
className="bg-amber-50 p-1 flex w-full border-b text-center justify-center link"
|
||||
style={{ backgroundColor: 'rgb(255 251 235)' }}
|
||||
onClick={() => searchStore.updateCurrentPage(1)}
|
||||
onClick={onShowNewSessions}
|
||||
>
|
||||
Show {numberWithCommas(count)} New {count > 1 ? 'Sessions' : 'Session'}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,57 +5,70 @@ import NoteItem from './NoteItem';
|
|||
import { observer } from 'mobx-react-lite';
|
||||
import { useStore } from 'App/mstore';
|
||||
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
|
||||
import NoteTags from 'Shared/SessionsTabOverview/components/Notes/NoteTags';
|
||||
|
||||
function NotesList() {
|
||||
const { notesStore } = useStore();
|
||||
|
||||
React.useEffect(() => {
|
||||
void notesStore.fetchNotes();
|
||||
void notesStore.fetchNotes();
|
||||
}, [notesStore.page]);
|
||||
|
||||
const list = notesStore.notes;
|
||||
|
||||
return (
|
||||
<Loader loading={notesStore.loading}>
|
||||
<NoContent
|
||||
show={list.length === 0}
|
||||
title={
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
{/* <Icon name="no-dashboard" size={80} color="figmaColors-accent-secondary" /> */}
|
||||
<AnimatedSVG name={ICONS.NO_NOTES} size={60} />
|
||||
<div className="text-center mt-4 text-lg font-medium">No notes yet</div>
|
||||
</div>
|
||||
}
|
||||
subtext={
|
||||
<div className="text-center flex justify-center items-center flex-col">
|
||||
Note observations during session replays and share them with your team.
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="border-b rounded bg-white">
|
||||
{list.map((note) => (
|
||||
<React.Fragment key={note.noteId}>
|
||||
<NoteItem note={note} />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
<>
|
||||
<div className="widget-wrapper">
|
||||
<div className="flex items-center px-4 py-1 justify-between w-full">
|
||||
<h2 className="text-2xl capitalize mr-4">Notes</h2>
|
||||
|
||||
<div className="w-full flex items-center justify-between py-4 px-6">
|
||||
<div className="text-disabled-text">
|
||||
Showing{' '}
|
||||
<span className="font-semibold">{Math.min(list.length, notesStore.pageSize)}</span> out
|
||||
of <span className="font-semibold">{notesStore.total}</span> notes
|
||||
<div className="flex items-center justify-end w-full">
|
||||
<NoteTags />
|
||||
</div>
|
||||
<Pagination
|
||||
page={notesStore.page}
|
||||
total={notesStore.total}
|
||||
onPageChange={(page) => notesStore.changePage(page)}
|
||||
limit={notesStore.pageSize}
|
||||
debounceRequest={100}
|
||||
/>
|
||||
</div>
|
||||
</NoContent>
|
||||
</Loader>
|
||||
<div className="border-b" />
|
||||
<Loader loading={notesStore.loading}>
|
||||
<NoContent
|
||||
show={list.length === 0}
|
||||
title={
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
{/* <Icon name="no-dashboard" size={80} color="figmaColors-accent-secondary" /> */}
|
||||
<AnimatedSVG name={ICONS.NO_NOTES} size={60} />
|
||||
<div className="text-center mt-4 text-lg font-medium">No notes yet</div>
|
||||
</div>
|
||||
}
|
||||
subtext={
|
||||
<div className="text-center flex justify-center items-center flex-col">
|
||||
Note observations during session replays and share them with your team.
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="border-b rounded bg-white">
|
||||
{list.map((note) => (
|
||||
<React.Fragment key={note.noteId}>
|
||||
<NoteItem note={note} />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="w-full flex items-center justify-between py-4 px-6">
|
||||
<div className="text-disabled-text">
|
||||
Showing{' '}
|
||||
<span className="font-semibold">{Math.min(list.length, notesStore.pageSize)}</span> out
|
||||
of <span className="font-semibold">{notesStore.total}</span> notes
|
||||
</div>
|
||||
<Pagination
|
||||
page={notesStore.page}
|
||||
total={notesStore.total}
|
||||
onPageChange={(page) => notesStore.changePage(page)}
|
||||
limit={notesStore.pageSize}
|
||||
debounceRequest={100}
|
||||
/>
|
||||
</div>
|
||||
</NoContent>
|
||||
</Loader>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import React, { useMemo } from 'react';
|
|||
import Period from 'Types/app/period';
|
||||
import SelectDateRange from 'Shared/SelectDateRange';
|
||||
import SessionTags from '../SessionTags';
|
||||
import NoteTags from '../Notes/NoteTags';
|
||||
import SessionSort from '../SessionSort';
|
||||
import { Space } from 'antd';
|
||||
import { useStore } from 'App/mstore';
|
||||
|
|
@ -17,9 +16,6 @@ function SessionHeader() {
|
|||
const period = Period({ start: startDate, end: endDate, rangeName: rangeValue });
|
||||
|
||||
const title = useMemo(() => {
|
||||
if (activeTab.type === 'notes') {
|
||||
return 'Notes';
|
||||
}
|
||||
if (activeTab.type === 'bookmarks') {
|
||||
return isEnterprise ? 'Vault' : 'Bookmarks';
|
||||
}
|
||||
|
|
@ -35,26 +31,15 @@ function SessionHeader() {
|
|||
return (
|
||||
<div className="flex items-center px-4 py-1 justify-between w-full">
|
||||
<h2 className="text-2xl capitalize mr-4">{title}</h2>
|
||||
{activeTab.type !== 'notes' ? (
|
||||
<div className="flex items-center w-full justify-end">
|
||||
{activeTab.type !== 'bookmarks' && (
|
||||
<>
|
||||
<SessionTags />
|
||||
<div className="mr-auto" />
|
||||
<Space>
|
||||
<SelectDateRange isAnt period={period} onChange={onDateChange} right={true} />
|
||||
<SessionSort />
|
||||
</Space>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{activeTab.type === 'notes' && (
|
||||
<div className="flex items-center justify-end w-full">
|
||||
<NoteTags />
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center w-full justify-end">
|
||||
{activeTab.type !== 'bookmarks' && <SessionTags />}
|
||||
<div className="mr-auto" />
|
||||
<Space>
|
||||
{activeTab.type !== 'bookmarks' &&
|
||||
<SelectDateRange isAnt period={period} onChange={onDateChange} right={true} />}
|
||||
<SessionSort />
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,7 +73,6 @@ function SessionList() {
|
|||
}, [isBookmark, isVault, activeTab, location.pathname]);
|
||||
const [statusData, setStatusData] = React.useState<SessionStatus>({ status: 0, count: 0 });
|
||||
|
||||
|
||||
const fetchStatus = async () => {
|
||||
const response = await sessionService.getRecordingStatus();
|
||||
setStatusData({
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
import { DateTime, Duration } from 'luxon'; // TODO
|
||||
import { Timezone } from 'App/mstore/types/sessionSettings';
|
||||
import { LAST_24_HOURS, LAST_30_DAYS, LAST_7_DAYS } from 'Types/app/period';
|
||||
import { CUSTOM_RANGE } from '@/dateRange';
|
||||
|
||||
export function getDateFromString(date: string, format = 'yyyy-MM-dd HH:mm:ss:SSS'): string {
|
||||
return DateTime.fromISO(date).toFormat(format);
|
||||
|
|
@ -191,3 +193,35 @@ export const countDaysFrom = (timestamp: number): number => {
|
|||
const d = new Date();
|
||||
return Math.round(Math.abs(d.getTime() - date.toJSDate().getTime()) / (1000 * 3600 * 24));
|
||||
}
|
||||
|
||||
export const getDateRangeUTC = (rangeName: string, customStartDate?: number, customEndDate?: number): {
|
||||
startDate: number;
|
||||
endDate: number
|
||||
} => {
|
||||
let endDate = new Date().getTime();
|
||||
let startDate: number;
|
||||
|
||||
switch (rangeName) {
|
||||
case LAST_7_DAYS:
|
||||
startDate = endDate - 7 * 24 * 60 * 60 * 1000;
|
||||
break;
|
||||
case LAST_30_DAYS:
|
||||
startDate = endDate - 30 * 24 * 60 * 60 * 1000;
|
||||
break;
|
||||
case CUSTOM_RANGE:
|
||||
if (!customStartDate || !customEndDate) {
|
||||
throw new Error('Start date and end date must be provided for CUSTOM_RANGE.');
|
||||
}
|
||||
startDate = customStartDate;
|
||||
endDate = customEndDate;
|
||||
break;
|
||||
case LAST_24_HOURS:
|
||||
default:
|
||||
startDate = endDate - 24 * 60 * 60 * 1000;
|
||||
}
|
||||
|
||||
return {
|
||||
startDate,
|
||||
endDate
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,46 +6,59 @@ import Search from '@/mstore/types/search';
|
|||
import { getFilterFromJson } from 'Types/filter/newFilter';
|
||||
|
||||
interface Props {
|
||||
onBeforeLoad?: () => Promise<any>;
|
||||
appliedFilter: any;
|
||||
onBeforeLoad?: () => Promise<void>;
|
||||
appliedFilter: Record<string, any>;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
const useSessionSearchQueryHandler = (props: Props) => {
|
||||
const useSessionSearchQueryHandler = ({ onBeforeLoad, appliedFilter, loading }: Props) => {
|
||||
const { searchStore } = useStore();
|
||||
const [beforeHookLoaded, setBeforeHookLoaded] = useState(!props.onBeforeLoad);
|
||||
const { appliedFilter, loading } = props;
|
||||
const [beforeHookLoaded, setBeforeHookLoaded] = useState(!onBeforeLoad);
|
||||
const history = useHistory();
|
||||
|
||||
// Apply filter from the query string when the component mounts
|
||||
useEffect(() => {
|
||||
const applyFilterFromQuery = async () => {
|
||||
if (!loading && !searchStore.urlParsed) {
|
||||
if (props.onBeforeLoad) {
|
||||
await props.onBeforeLoad();
|
||||
setBeforeHookLoaded(true);
|
||||
}
|
||||
try {
|
||||
if (onBeforeLoad) {
|
||||
await onBeforeLoad();
|
||||
setBeforeHookLoaded(true);
|
||||
}
|
||||
|
||||
const converter = JsonUrlConverter.urlParamsToJson(history.location.search);
|
||||
const json: any = getFilterFromJson(converter.toJSON());
|
||||
const filter = new Search(json);
|
||||
searchStore.applyFilter(filter, true);
|
||||
searchStore.setUrlParsed()
|
||||
const converter = JsonUrlConverter.urlParamsToJson(history.location.search);
|
||||
const json = getFilterFromJson(converter.toJSON());
|
||||
const filter = new Search(json);
|
||||
searchStore.applyFilter(filter, true);
|
||||
searchStore.setUrlParsed();
|
||||
} catch (error) {
|
||||
console.error('Error applying filter from query:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void applyFilterFromQuery();
|
||||
}, [loading]);
|
||||
}, [loading, onBeforeLoad, searchStore, history.location.search]);
|
||||
|
||||
// Update the URL whenever the appliedFilter changes
|
||||
useEffect(() => {
|
||||
const generateUrlQuery = () => {
|
||||
const updateUrlWithFilter = () => {
|
||||
if (!loading && beforeHookLoaded) {
|
||||
const converter = JsonUrlConverter.jsonToUrlParams(appliedFilter);
|
||||
history.replace({ search: converter });
|
||||
const query = JsonUrlConverter.jsonToUrlParams(appliedFilter);
|
||||
history.replace({ search: query });
|
||||
}
|
||||
};
|
||||
|
||||
generateUrlQuery();
|
||||
}, [appliedFilter, loading, beforeHookLoaded]);
|
||||
updateUrlWithFilter();
|
||||
}, [appliedFilter, loading, beforeHookLoaded, history]);
|
||||
|
||||
// Ensure the URL syncs on remount if already parsed
|
||||
useEffect(() => {
|
||||
if (searchStore.urlParsed) {
|
||||
const query = JsonUrlConverter.jsonToUrlParams(appliedFilter);
|
||||
history.replace({ search: query });
|
||||
}
|
||||
}, [appliedFilter, searchStore.urlParsed, history]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export default class FilterStore {
|
|||
}
|
||||
|
||||
setTopValues = (key: string, values: TopValue[]) => {
|
||||
this.topValues[key] = values.filter((value) => value !== null && value.value !== '');
|
||||
this.topValues[key] = values?.filter((value) => value !== null && value.value !== '');
|
||||
};
|
||||
|
||||
fetchTopValues = async (key: string, source?: string) => {
|
||||
|
|
|
|||
|
|
@ -162,15 +162,14 @@ class SearchStore {
|
|||
});
|
||||
}
|
||||
|
||||
updateCurrentPage(page: number) {
|
||||
updateCurrentPage(page: number, force = false) {
|
||||
this.currentPage = page;
|
||||
void this.fetchSessions();
|
||||
void this.fetchSessions(force);
|
||||
}
|
||||
|
||||
setActiveTab(tab: string) {
|
||||
runInAction(() => {
|
||||
this.activeTab = TAB_MAP[tab];
|
||||
this.currentPage = 1;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -229,12 +228,13 @@ class SearchStore {
|
|||
if (this.latestRequestTime) {
|
||||
const period = Period({ rangeName: CUSTOM_RANGE, start: this.latestRequestTime, end: Date.now() });
|
||||
const newTimestamps: any = period.toJSON();
|
||||
filter.startTimestamp = newTimestamps.startDate;
|
||||
filter.endTimestamp = newTimestamps.endDate;
|
||||
filter.startDate = newTimestamps.startDate;
|
||||
filter.endDate = newTimestamps.endDate;
|
||||
}
|
||||
searchService.checkLatestSessions(filter).then((response: any) => {
|
||||
this.latestList = response;
|
||||
this.latestRequestTime = Date.now();
|
||||
runInAction(() => {
|
||||
this.latestList = List(response);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -264,8 +264,10 @@ class SearchStore {
|
|||
});
|
||||
}
|
||||
|
||||
this.currentPage = 1;
|
||||
|
||||
if (filter.value && filter.value[0] && filter.value[0] !== '') {
|
||||
this.fetchSessions();
|
||||
void this.fetchSessions();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -336,6 +338,9 @@ class SearchStore {
|
|||
filter.filters = filter.filters.concat(tagFilter);
|
||||
}
|
||||
|
||||
this.latestRequestTime = Date.now();
|
||||
this.latestList = List();
|
||||
|
||||
await sessionStore.fetchSessions({
|
||||
...filter,
|
||||
page: this.currentPage,
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ import { FilterCategory, FilterKey } from 'Types/filter/filterType';
|
|||
import {
|
||||
filtersMap,
|
||||
generateFilterOptions,
|
||||
liveFiltersMap,
|
||||
liveFiltersMap
|
||||
} from 'Types/filter/newFilter';
|
||||
import { List } from 'immutable';
|
||||
import { makeAutoObservable } from 'mobx';
|
||||
import { makeAutoObservable, reaction } from 'mobx';
|
||||
import Search from 'App/mstore/types/search';
|
||||
import { checkFilterValue, IFilter } from 'App/mstore/types/filter';
|
||||
import FilterItem from 'App/mstore/types/filterItem';
|
||||
|
|
@ -63,11 +63,29 @@ class SearchStoreLive {
|
|||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
|
||||
// Reset currentPage to 1 only on filter changes
|
||||
reaction(
|
||||
() => this.instance,
|
||||
() => {
|
||||
this.currentPage = 1;
|
||||
void this.fetchSessions();
|
||||
}
|
||||
);
|
||||
|
||||
// Fetch sessions when currentPage changes
|
||||
reaction(
|
||||
() => this.currentPage,
|
||||
() => {
|
||||
void this.fetchSessions();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
get filterList() {
|
||||
return generateFilterOptions(filtersMap);
|
||||
}
|
||||
|
||||
get filterListLive() {
|
||||
return generateFilterOptions(liveFiltersMap);
|
||||
}
|
||||
|
|
@ -96,14 +114,12 @@ class SearchStoreLive {
|
|||
|
||||
edit(instance: Partial<Search>) {
|
||||
this.instance = new Search(Object.assign({ ...this.instance }, instance));
|
||||
this.currentPage = 1;
|
||||
}
|
||||
|
||||
|
||||
apply(filter: any, fromUrl: boolean) {
|
||||
if (fromUrl) {
|
||||
this.instance = new Search(filter);
|
||||
this.currentPage = 1;
|
||||
} else {
|
||||
this.instance = { ...this.instance, ...filter };
|
||||
}
|
||||
|
|
@ -115,7 +131,6 @@ class SearchStoreLive {
|
|||
|
||||
updateCurrentPage(page: number) {
|
||||
this.currentPage = page;
|
||||
this.fetchSessions();
|
||||
}
|
||||
|
||||
clearSearch() {
|
||||
|
|
@ -140,22 +155,25 @@ class SearchStoreLive {
|
|||
: null;
|
||||
|
||||
if (index > -1) {
|
||||
const oldFilter = this.instance.filters[index];
|
||||
const updatedFilter = {
|
||||
...oldFilter,
|
||||
value: oldFilter.value.concat(filter.value)
|
||||
// Update existing filter
|
||||
// @ts-ignore
|
||||
this.instance.filters[index] = {
|
||||
...this.instance.filters[index],
|
||||
value: this.instance.filters[index].value.concat(filter.value)
|
||||
};
|
||||
oldFilter.merge(updatedFilter);
|
||||
} else {
|
||||
this.instance.filters.push(filter);
|
||||
this.instance = new Search({
|
||||
...this.instance.toData()
|
||||
});
|
||||
// Add new filter (create a new array reference to notify MobX)
|
||||
this.instance.filters = [...this.instance.filters, filter];
|
||||
}
|
||||
|
||||
if (filter.value && filter.value[0] && filter.value[0] !== '') {
|
||||
this.fetchSessions();
|
||||
}
|
||||
// Update the instance to trigger reactions
|
||||
this.instance = new Search({
|
||||
...this.instance.toData()
|
||||
});
|
||||
|
||||
// if (filter.value && filter.value[0] && filter.value[0] !== '') {
|
||||
// void this.fetchSessions();
|
||||
// }
|
||||
}
|
||||
|
||||
addFilterByKeyAndValue(key: any, value: any, operator?: string, sourceOperator?: string, source?: string) {
|
||||
|
|
@ -200,7 +218,7 @@ class SearchStoreLive {
|
|||
};
|
||||
|
||||
async fetchSessions() {
|
||||
await sessionStore.fetchLiveSessions(this.instance.toSearch());
|
||||
await sessionStore.fetchLiveSessions({ ...this.instance.toSearch(), page: this.currentPage });
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ export default class SessionStore {
|
|||
}
|
||||
const nextEntryNum =
|
||||
keys.length > 0
|
||||
? Math.max(...keys.map((key) => this.prefetchedMobUrls[key].entryNum || 0)) + 1
|
||||
? Math.max(...keys.map((key) => this.prefetchedMobUrls[key]?.entryNum || 0)) + 1
|
||||
: 0;
|
||||
this.prefetchedMobUrls[sessionId] = {
|
||||
data: fileData,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { DATE_RANGE_VALUES, CUSTOM_RANGE, getDateRangeFromValue } from 'App/dateRange';
|
||||
import Filter, { checkFilterValue, IFilter } from 'App/mstore/types/filter';
|
||||
import { CUSTOM_RANGE, DATE_RANGE_VALUES, getDateRangeFromValue } from 'App/dateRange';
|
||||
import Filter, { IFilter } from 'App/mstore/types/filter';
|
||||
import FilterItem from 'App/mstore/types/filterItem';
|
||||
import { action, makeAutoObservable, observable } from 'mobx';
|
||||
import { makeAutoObservable, observable } from 'mobx';
|
||||
import { LAST_24_HOURS, LAST_30_DAYS, LAST_7_DAYS } from 'Types/app/period';
|
||||
|
||||
// @ts-ignore
|
||||
const rangeValue = DATE_RANGE_VALUES.LAST_24_HOURS;
|
||||
|
|
@ -69,7 +70,7 @@ export default class Search {
|
|||
|
||||
constructor(initialData?: Partial<ISearch>) {
|
||||
makeAutoObservable(this, {
|
||||
filters: observable,
|
||||
filters: observable
|
||||
});
|
||||
Object.assign(this, {
|
||||
name: '',
|
||||
|
|
@ -142,11 +143,48 @@ export default class Search {
|
|||
return new FilterItem(filter).toJson();
|
||||
});
|
||||
|
||||
const { startDate, endDate } = this.getDateRange(js.rangeValue, js.startDate, js.endDate);
|
||||
js.startDate = startDate;
|
||||
js.endDate = endDate;
|
||||
|
||||
delete js.createdAt;
|
||||
delete js.key;
|
||||
return js;
|
||||
}
|
||||
|
||||
private getDateRange(rangeName: string, customStartDate: number, customEndDate: number): {
|
||||
startDate: number;
|
||||
endDate: number
|
||||
} {
|
||||
let endDate = new Date().getTime();
|
||||
let startDate: number;
|
||||
|
||||
switch (rangeName) {
|
||||
case LAST_7_DAYS:
|
||||
startDate = endDate - 7 * 24 * 60 * 60 * 1000;
|
||||
break;
|
||||
case LAST_30_DAYS:
|
||||
startDate = endDate - 30 * 24 * 60 * 60 * 1000;
|
||||
break;
|
||||
case CUSTOM_RANGE:
|
||||
if (!customStartDate || !customEndDate) {
|
||||
throw new Error('Start date and end date must be provided for CUSTOM_RANGE.');
|
||||
}
|
||||
startDate = customStartDate;
|
||||
endDate = customEndDate;
|
||||
break;
|
||||
case LAST_24_HOURS:
|
||||
default:
|
||||
startDate = endDate - 24 * 60 * 60 * 1000;
|
||||
}
|
||||
|
||||
return {
|
||||
startDate,
|
||||
endDate
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
fromJS({ eventsOrder, filters, events, custom, ...filterData }: any) {
|
||||
let startDate, endDate;
|
||||
const rValue = filterData.rangeValue || rangeValue;
|
||||
|
|
@ -176,3 +214,4 @@ export default class Search {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,11 +66,16 @@ export default class UiPlayerStore {
|
|||
endTs: 0,
|
||||
}
|
||||
zoomTab: 'overview' | 'journey' | 'issues' | 'errors' = 'overview'
|
||||
dataSource: 'all' | 'current' = 'all'
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
changeDataSource = (source: 'all' | 'current') => {
|
||||
this.dataSource = source;
|
||||
}
|
||||
|
||||
toggleFullscreen = (val?: boolean) => {
|
||||
this.fullscreen = val ?? !this.fullscreen;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,6 @@ class UserStore {
|
|||
get isEnterprise() {
|
||||
return (
|
||||
this.account?.edition === 'ee' ||
|
||||
this.account?.edition === 'msaas' ||
|
||||
this.authStore.authDetails?.edition === 'ee'
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,18 @@
|
|||
import { Store } from './types'
|
||||
|
||||
export default class SimpleSore<G extends Object, S extends Object = G> implements Store<G, S> {
|
||||
export default class SimpleStore<G extends Record<string, any>, S extends Record<string, any> = G> implements Store<G, S> {
|
||||
constructor(private state: G){}
|
||||
get(): G {
|
||||
return this.state
|
||||
}
|
||||
update(newState: Partial<S>) {
|
||||
update = (newState: Partial<S>) => {
|
||||
Object.assign(this.state, newState)
|
||||
}
|
||||
updateTabStates = (id: string, newState: Partial<S>) => {
|
||||
try {
|
||||
Object.assign(this.state.tabStates[id], newState)
|
||||
} catch (e) {
|
||||
console.log('Error updating tab state', e, id, newState, this.state, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ export interface Interval {
|
|||
export interface Store<G extends Object, S extends Object = G> {
|
||||
get(): G
|
||||
update(state: Partial<S>): void
|
||||
updateTabStates(id: string, state: Partial<S>): void
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -236,6 +236,7 @@ export default class MessageLoader {
|
|||
try {
|
||||
await this.loadMobs();
|
||||
} catch (sessionLoadError) {
|
||||
console.info('!', sessionLoadError);
|
||||
try {
|
||||
await this.loadEFSMobs();
|
||||
} catch (unprocessedLoadError) {
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ export default class MessageManager {
|
|||
closedTabs: [],
|
||||
sessionStart: 0,
|
||||
tabNames: {},
|
||||
};
|
||||
};
|
||||
|
||||
private clickManager: ListWalker<MouseClick> = new ListWalker();
|
||||
private mouseThrashingManager: ListWalker<MouseThrashing> = new ListWalker();
|
||||
|
|
@ -179,6 +179,7 @@ export default class MessageManager {
|
|||
this.activityManager.end();
|
||||
this.state.update({ skipIntervals: this.activityManager.list });
|
||||
}
|
||||
|
||||
Object.values(this.tabs).forEach((tab) => tab.onFileReadSuccess?.());
|
||||
};
|
||||
|
||||
|
|
@ -317,6 +318,7 @@ export default class MessageManager {
|
|||
if (msg.tp === 9999) return;
|
||||
if (!this.tabs[msg.tabId]) {
|
||||
this.tabsAmount++;
|
||||
this.state.update({ tabStates: { ...this.state.get().tabStates, [msg.tabId]: TabSessionManager.INITIAL_STATE } });
|
||||
this.tabs[msg.tabId] = new TabSessionManager(
|
||||
this.session,
|
||||
this.state,
|
||||
|
|
|
|||
|
|
@ -163,15 +163,7 @@ export default class TabSessionManager {
|
|||
* Because we use main state (from messageManager), we have to update it this way
|
||||
* */
|
||||
updateLocalState(state: Partial<TabState>) {
|
||||
this.state.update({
|
||||
tabStates: {
|
||||
...this.state.get().tabStates,
|
||||
[this.id]: {
|
||||
...this.state.get().tabStates[this.id],
|
||||
...state,
|
||||
},
|
||||
},
|
||||
});
|
||||
this.state.updateTabStates(this.id, state);
|
||||
}
|
||||
|
||||
private setCSSLoading = (cssLoading: boolean) => {
|
||||
|
|
@ -202,6 +194,7 @@ export default class TabSessionManager {
|
|||
);
|
||||
}
|
||||
|
||||
firstTitleSet = false
|
||||
distributeMessage(msg: Message): void {
|
||||
this.lastMessageTs = msg.time;
|
||||
switch (msg.tp) {
|
||||
|
|
@ -242,6 +235,10 @@ export default class TabSessionManager {
|
|||
case MType.SetPageLocationDeprecated:
|
||||
case MType.SetPageLocation:
|
||||
this.locationManager.append(msg);
|
||||
if ('documentTitle' in msg && !this.firstTitleSet) {
|
||||
this.state.update({ tabNames: { ...this.state.get().tabNames, [this.id]: msg.documentTitle } });
|
||||
this.firstTitleSet = true
|
||||
}
|
||||
if (msg.navigationStart > 0) {
|
||||
this.loadedLocationManager.append(msg);
|
||||
}
|
||||
|
|
@ -414,8 +411,9 @@ export default class TabSessionManager {
|
|||
}
|
||||
|
||||
Object.assign(stateToUpdate, this.lists.moveGetState(t));
|
||||
Object.keys(stateToUpdate).length > 0 &&
|
||||
if (Object.keys(stateToUpdate).length > 0) {
|
||||
this.updateLocalState(stateToUpdate);
|
||||
}
|
||||
/* Sequence of the managers is important here */
|
||||
// Preparing the size of "screen"
|
||||
const lastResize = this.resizeManager.moveGetLast(t, index);
|
||||
|
|
|
|||
|
|
@ -436,3 +436,7 @@ p {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ant-segmented-item{
|
||||
border-radius: .5rem !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import Period, { CUSTOM_RANGE } from 'Types/app/period';
|
||||
import { filtersMap } from 'Types/filter/newFilter';
|
||||
import Period, { CUSTOM_RANGE, LAST_24_HOURS } from 'Types/app/period';
|
||||
|
||||
const DEFAULT_SORT = 'startTs';
|
||||
const DEFAULT_ORDER = 'desc';
|
||||
const DEFAULT_EVENTS_ORDER = 'then';
|
||||
|
||||
class Filter {
|
||||
key: string;
|
||||
|
|
@ -25,24 +27,28 @@ class Filter {
|
|||
}
|
||||
}
|
||||
|
||||
class InputJson {
|
||||
export class InputJson {
|
||||
filters: Filter[];
|
||||
rangeValue: string;
|
||||
startDate: number;
|
||||
endDate: number;
|
||||
startDate?: number;
|
||||
endDate?: number;
|
||||
sort: string;
|
||||
order: string;
|
||||
eventsOrder: string;
|
||||
|
||||
constructor(filters: Filter[], rangeValue: string, startDate: number, endDate: number, sort: string, order: string, eventsOrder: string) {
|
||||
constructor(
|
||||
filters: Filter[],
|
||||
rangeValue: string,
|
||||
sort: string,
|
||||
order: string,
|
||||
eventsOrder: string,
|
||||
startDate?: string | number,
|
||||
endDate?: string | number
|
||||
) {
|
||||
this.filters = filters;
|
||||
// .map((f: any) => {
|
||||
// const subFilters = f.filters ? f.filters.map((sf: any) => new Filter(sf.key, sf.operator, sf.value, sf.filters)) : undefined;
|
||||
// return new Filter(f.key, f.operator, f.value, subFilters);
|
||||
// });
|
||||
this.rangeValue = rangeValue;
|
||||
this.startDate = startDate;
|
||||
this.endDate = endDate;
|
||||
this.startDate = startDate ? +startDate : undefined;
|
||||
this.endDate = endDate ? +endDate : undefined;
|
||||
this.sort = sort;
|
||||
this.order = order;
|
||||
this.eventsOrder = eventsOrder;
|
||||
|
|
@ -50,17 +56,28 @@ class InputJson {
|
|||
|
||||
toJSON() {
|
||||
return {
|
||||
filters: this.filters.map(f => f.toJSON()),
|
||||
filters: this.filters.map((f) => f.toJSON()),
|
||||
rangeValue: this.rangeValue,
|
||||
startDate: this.startDate,
|
||||
endDate: this.endDate,
|
||||
startDate: this.startDate ?? null,
|
||||
endDate: this.endDate ?? null,
|
||||
sort: this.sort,
|
||||
order: this.order,
|
||||
eventsOrder: this.eventsOrder
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fromJSON(json: Record<string, any>): InputJson {
|
||||
return new InputJson(
|
||||
json.filters.map((f: any) => new Filter(f.key, f.operator, f.value, f.filters)),
|
||||
json.rangeValue,
|
||||
json.sort,
|
||||
json.order,
|
||||
json.eventsOrder,
|
||||
json.startDate,
|
||||
json.endDate
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class JsonUrlConverter {
|
||||
static keyMap = {
|
||||
|
|
@ -76,35 +93,46 @@ export class JsonUrlConverter {
|
|||
filters: 'f'
|
||||
};
|
||||
|
||||
static getDateRangeValues(rangeValue: string, startDate: number | undefined, endDate: number | undefined): [number, number] {
|
||||
if (rangeValue === 'CUSTOM_RANGE') {
|
||||
return [startDate!, endDate!];
|
||||
static getDateRangeValues(
|
||||
rangeValue: string,
|
||||
startDate: string | null,
|
||||
endDate: string | null
|
||||
): [string, string] {
|
||||
if (rangeValue === CUSTOM_RANGE) {
|
||||
return [startDate || '', endDate || ''];
|
||||
}
|
||||
const period = Period({ rangeName: rangeValue });
|
||||
const period: any = Period({ rangeName: rangeValue });
|
||||
return [period.start, period.end];
|
||||
}
|
||||
|
||||
static jsonToUrlParams(json: InputJson): string {
|
||||
static jsonToUrlParams(json: Record<string, any>): string {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
const addFilterParams = (filter: Filter, prefix: string) => {
|
||||
params.append(`${prefix}${this.keyMap.key}`, filter.key);
|
||||
params.append(`${prefix}${this.keyMap.operator}`, filter.operator);
|
||||
if (filter.value) {
|
||||
filter.value.forEach((v, i) => params.append(`${prefix}${this.keyMap.value}[${i}]`, v || ''));
|
||||
}
|
||||
if (filter.filters) {
|
||||
filter.filters.forEach((f, i) => addFilterParams(f, `${prefix}${this.keyMap.filters}[${i}].`));
|
||||
}
|
||||
filter.value?.forEach((v, i) =>
|
||||
params.append(`${prefix}${this.keyMap.value}[${i}]`, v || '')
|
||||
);
|
||||
filter.filters?.forEach((f, i) =>
|
||||
addFilterParams(f, `${prefix}${this.keyMap.filters}[${i}].`)
|
||||
);
|
||||
};
|
||||
|
||||
json.filters.forEach((filter, index) => addFilterParams(filter, `${this.keyMap.filters}[${index}].`));
|
||||
|
||||
const rangeValues = this.getDateRangeValues(json.rangeValue, json.startDate, json.endDate);
|
||||
json.filters.forEach((filter: any, index: number) =>
|
||||
addFilterParams(filter, `${this.keyMap.filters}[${index}].`)
|
||||
);
|
||||
|
||||
params.append(this.keyMap.rangeValue, json.rangeValue);
|
||||
params.append(this.keyMap.startDate, rangeValues[0].toString());
|
||||
params.append(this.keyMap.endDate, rangeValues[1].toString());
|
||||
if (json.rangeValue === CUSTOM_RANGE) {
|
||||
const rangeValues = this.getDateRangeValues(
|
||||
json.rangeValue,
|
||||
json.startDate?.toString() || null,
|
||||
json.endDate?.toString() || null
|
||||
);
|
||||
params.append(this.keyMap.startDate, rangeValues[0]);
|
||||
params.append(this.keyMap.endDate, rangeValues[1]);
|
||||
}
|
||||
params.append(this.keyMap.sort, json.sort);
|
||||
params.append(this.keyMap.order, json.order);
|
||||
params.append(this.keyMap.eventsOrder, json.eventsOrder);
|
||||
|
|
@ -130,7 +158,7 @@ export class JsonUrlConverter {
|
|||
filters.push(getFilterParams(`${prefix}${this.keyMap.filters}[${index}].`));
|
||||
index++;
|
||||
}
|
||||
return new Filter(key, operator, value.length ? value : '', filters.length ? filters : []);
|
||||
return new Filter(key, operator, value.length ? value : [], filters.length ? filters : []);
|
||||
};
|
||||
|
||||
const filters: Filter[] = [];
|
||||
|
|
@ -140,23 +168,22 @@ export class JsonUrlConverter {
|
|||
index++;
|
||||
}
|
||||
|
||||
const rangeValue = params.get(this.keyMap.rangeValue) || 'LAST_24_HOURS';
|
||||
const rangeValue = params.get(this.keyMap.rangeValue) || LAST_24_HOURS;
|
||||
const rangeValues = this.getDateRangeValues(rangeValue, params.get(this.keyMap.startDate), params.get(this.keyMap.endDate));
|
||||
const startDate = rangeValues[0];
|
||||
const endDate = rangeValues[1];
|
||||
|
||||
return new InputJson(
|
||||
filters,
|
||||
rangeValue,
|
||||
startDate,
|
||||
endDate,
|
||||
params.get(this.keyMap.sort) || 'startTs',
|
||||
params.get(this.keyMap.order) || 'desc',
|
||||
params.get(this.keyMap.eventsOrder) || 'then'
|
||||
params.get(this.keyMap.sort) || DEFAULT_SORT,
|
||||
params.get(this.keyMap.order) || DEFAULT_ORDER,
|
||||
params.get(this.keyMap.eventsOrder) || DEFAULT_EVENTS_ORDER,
|
||||
rangeValues[0],
|
||||
rangeValues[1]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Example usage
|
||||
// const urlParams = '?f[0].k=click&f[0].op=on&f[0].v[0]=Refresh&f[1].k=fetch&f[1].op=is&f[1].v[0]=&f[1].f[0].k=fetchUrl&f[1].f[0].op=is&f[1].f[0].v[0]=/g/collect&f[1].f[1].k=fetchStatusCode&f[1].f[1].op=>=&f[1].f[1].v[0]=400&f[1].f[2].k=fetchMethod&f[1].f[2].op=is&f[1].f[2].v[0]=&f[1].f[3].k=fetchDuration&f[1].f[3].op==&f[1].f[3].v[0]=&f[1].f[4].k=fetchRequestBody&f[1].f[4].op=is&f[1].f[4].v[0]=&f[1].f[5].k=fetchResponseBody&f[1].f[5].op=is&f[1].f[5].v[0]=&rv=LAST_24_HOURS&sd=1731343412555&ed=1731429812555&s=startTs&o=desc&st=false&eo=then';
|
||||
// const parsedJson = JsonUrlConverter.urlParamsToJson(urlParams);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export function validateIP(value) {
|
|||
|
||||
export function validateURL(value) {
|
||||
if (typeof value !== 'string') return false;
|
||||
const urlRegex = /^(http|https):\/\/(?:www\.)?[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}(\/\S*)?$/i;
|
||||
const urlRegex = /^(http|https):\/\/(?:www\.)?[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}(:\d+)?(\/\S*)?$/i;
|
||||
const ipRegex = /^(http|https):\/\/(?:localhost|(\d{1,3}\.){3}\d{1,3})(:\d+)?(\/\S*)?$/i;
|
||||
return urlRegex.test(value) || ipRegex.test(value);
|
||||
}
|
||||
|
|
@ -89,4 +89,4 @@ export const validatePassword = (password) => {
|
|||
const regex =
|
||||
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?])[A-Za-z\d!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]{8,}$/;
|
||||
return regex.test(password);
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
"@babel/plugin-transform-private-methods": "^7.23.3",
|
||||
"@floating-ui/react-dom-interactions": "^0.10.3",
|
||||
"@medv/finder": "^3.1.0",
|
||||
"@sentry/browser": "^5.21.1",
|
||||
"@sentry/browser": "^8.34.0",
|
||||
"@svg-maps/world": "^1.0.1",
|
||||
"@tanstack/react-query": "^5.56.2",
|
||||
"@wojtekmaj/react-daterange-picker": "^6.0.0",
|
||||
|
|
@ -91,7 +91,7 @@
|
|||
"@babel/preset-typescript": "^7.23.2",
|
||||
"@babel/runtime": "^7.23.2",
|
||||
"@jest/globals": "^29.7.0",
|
||||
"@openreplay/sourcemap-uploader": "^3.0.8",
|
||||
"@openreplay/sourcemap-uploader": "^3.0.10",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@types/luxon": "^3.4.2",
|
||||
"@types/node": "^22.7.8",
|
||||
|
|
@ -116,6 +116,7 @@
|
|||
"cypress": "^13.3.0",
|
||||
"cypress-image-snapshot": "^4.0.1",
|
||||
"dotenv": "^6.2.0",
|
||||
"esbuild-loader": "^4.2.2",
|
||||
"eslint": "^8.15.0",
|
||||
"eslint-plugin-react": "^7.29.4",
|
||||
"file-loader": "^6.2.0",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import CopyWebpackPlugin from 'copy-webpack-plugin';
|
|||
import HtmlWebpackPlugin from "html-webpack-plugin";
|
||||
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
||||
import CompressionPlugin from "compression-webpack-plugin";
|
||||
import { EsbuildPlugin } from 'esbuild-loader';
|
||||
|
||||
const dotenv = require('dotenv').config({ path: __dirname + '/.env' })
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
const stylesHandler = MiniCssExtractPlugin.loader;
|
||||
|
|
@ -28,23 +30,32 @@ const config: Configuration = {
|
|||
splitChunks: {
|
||||
chunks: 'all',
|
||||
},
|
||||
minimizer: [
|
||||
new EsbuildPlugin({
|
||||
target: 'es2020',
|
||||
css: true
|
||||
})
|
||||
]
|
||||
},
|
||||
module: {
|
||||
exprContextCritical: false,
|
||||
rules: [
|
||||
{
|
||||
test: /\.(ts|js)x?$/i,
|
||||
test: /\.tsx?$/i,
|
||||
exclude: isDevelopment ? /node_modules/ : undefined,
|
||||
use: ['thread-loader', {
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
presets: [
|
||||
"@babel/preset-env",
|
||||
"@babel/preset-react",
|
||||
"@babel/preset-typescript",
|
||||
],
|
||||
},
|
||||
}],
|
||||
loader: "esbuild-loader",
|
||||
options: {
|
||||
target: 'es2020',
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.jsx?$/i,
|
||||
exclude: isDevelopment ? /node_modules/ : undefined,
|
||||
loader: "esbuild-loader",
|
||||
options: {
|
||||
loader: 'jsx',
|
||||
target: 'es2020',
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.s[ac]ss$/i,
|
||||
|
|
@ -111,7 +122,11 @@ const config: Configuration = {
|
|||
},
|
||||
},
|
||||
plugins: [
|
||||
new CompressionPlugin(),
|
||||
(isDevelopment ? false : new CompressionPlugin({
|
||||
test: /\.(js|css|html|svg)$/,
|
||||
algorithm: 'brotliCompress',
|
||||
threshold: 10240,
|
||||
})),
|
||||
new webpack.DefinePlugin({
|
||||
// 'process.env': ENV_VARIABLES,
|
||||
'window.env': ENV_VARIABLES,
|
||||
|
|
@ -131,6 +146,7 @@ const config: Configuration = {
|
|||
performance: {
|
||||
hints: false,
|
||||
},
|
||||
watchOptions: { ignored: "**/node_modules/**" },
|
||||
devServer: {
|
||||
// static: path.join(__dirname, "public"),
|
||||
historyApiFallback: true,
|
||||
|
|
@ -138,7 +154,6 @@ const config: Configuration = {
|
|||
open: true,
|
||||
port: 3333,
|
||||
hot: true,
|
||||
compress: true,
|
||||
allowedHosts: "all",
|
||||
client: {
|
||||
overlay: {
|
||||
|
|
|
|||
|
|
@ -1898,6 +1898,167 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/aix-ppc64@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/aix-ppc64@npm:0.21.5"
|
||||
conditions: os=aix & cpu=ppc64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/android-arm64@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/android-arm64@npm:0.21.5"
|
||||
conditions: os=android & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/android-arm@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/android-arm@npm:0.21.5"
|
||||
conditions: os=android & cpu=arm
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/android-x64@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/android-x64@npm:0.21.5"
|
||||
conditions: os=android & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/darwin-arm64@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/darwin-arm64@npm:0.21.5"
|
||||
conditions: os=darwin & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/darwin-x64@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/darwin-x64@npm:0.21.5"
|
||||
conditions: os=darwin & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/freebsd-arm64@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/freebsd-arm64@npm:0.21.5"
|
||||
conditions: os=freebsd & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/freebsd-x64@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/freebsd-x64@npm:0.21.5"
|
||||
conditions: os=freebsd & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/linux-arm64@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/linux-arm64@npm:0.21.5"
|
||||
conditions: os=linux & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/linux-arm@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/linux-arm@npm:0.21.5"
|
||||
conditions: os=linux & cpu=arm
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/linux-ia32@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/linux-ia32@npm:0.21.5"
|
||||
conditions: os=linux & cpu=ia32
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/linux-loong64@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/linux-loong64@npm:0.21.5"
|
||||
conditions: os=linux & cpu=loong64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/linux-mips64el@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/linux-mips64el@npm:0.21.5"
|
||||
conditions: os=linux & cpu=mips64el
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/linux-ppc64@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/linux-ppc64@npm:0.21.5"
|
||||
conditions: os=linux & cpu=ppc64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/linux-riscv64@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/linux-riscv64@npm:0.21.5"
|
||||
conditions: os=linux & cpu=riscv64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/linux-s390x@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/linux-s390x@npm:0.21.5"
|
||||
conditions: os=linux & cpu=s390x
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/linux-x64@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/linux-x64@npm:0.21.5"
|
||||
conditions: os=linux & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/netbsd-x64@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/netbsd-x64@npm:0.21.5"
|
||||
conditions: os=netbsd & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/openbsd-x64@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/openbsd-x64@npm:0.21.5"
|
||||
conditions: os=openbsd & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/sunos-x64@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/sunos-x64@npm:0.21.5"
|
||||
conditions: os=sunos & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/win32-arm64@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/win32-arm64@npm:0.21.5"
|
||||
conditions: os=win32 & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/win32-ia32@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/win32-ia32@npm:0.21.5"
|
||||
conditions: os=win32 & cpu=ia32
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/win32-x64@npm:0.21.5":
|
||||
version: 0.21.5
|
||||
resolution: "@esbuild/win32-x64@npm:0.21.5"
|
||||
conditions: os=win32 & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint-community/eslint-utils@npm:^4.2.0":
|
||||
version: 4.4.1
|
||||
resolution: "@eslint-community/eslint-utils@npm:4.4.1"
|
||||
|
|
@ -2471,15 +2632,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@openreplay/sourcemap-uploader@npm:^3.0.8":
|
||||
version: 3.0.10
|
||||
resolution: "@openreplay/sourcemap-uploader@npm:3.0.10"
|
||||
"@openreplay/sourcemap-uploader@npm:^3.0.10":
|
||||
version: 3.0.13
|
||||
resolution: "@openreplay/sourcemap-uploader@npm:3.0.13"
|
||||
dependencies:
|
||||
argparse: "npm:^2.0.1"
|
||||
glob: "npm:^8.0.3"
|
||||
glob-promise: "npm:^6.0.7"
|
||||
bin:
|
||||
sourcemap-uploader: cli.js
|
||||
checksum: 10c1/aa22a161020f55e96c3835cb719f4c530728ccb55e90d1a0bbc96c5243bffb900e204dd6275f1dee2927db3c4439b5d33c38c60774e5340ccb36acab95f30cc5
|
||||
checksum: 10c1/1bab68b1a2f348973d9c027e00b9e027a833cec998e9a8a6c2872585d46fcdfb6f1492b98fd42f04ed2e6c4f913ec19e44a24be95460e1f7d1e08f6edf15eabe
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -7001,6 +7163,100 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"esbuild-loader@npm:^4.2.2":
|
||||
version: 4.2.2
|
||||
resolution: "esbuild-loader@npm:4.2.2"
|
||||
dependencies:
|
||||
esbuild: "npm:^0.21.0"
|
||||
get-tsconfig: "npm:^4.7.0"
|
||||
loader-utils: "npm:^2.0.4"
|
||||
webpack-sources: "npm:^1.4.3"
|
||||
peerDependencies:
|
||||
webpack: ^4.40.0 || ^5.0.0
|
||||
checksum: 10c1/2e29724312e75ffdb06d6421536078f36c135e9d8563bd413d78b061bf24459e06661f6baadd11a7f8e630e22abdf0d9d18921d51460bd2eeee47e000e29fd17
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"esbuild@npm:^0.21.0":
|
||||
version: 0.21.5
|
||||
resolution: "esbuild@npm:0.21.5"
|
||||
dependencies:
|
||||
"@esbuild/aix-ppc64": "npm:0.21.5"
|
||||
"@esbuild/android-arm": "npm:0.21.5"
|
||||
"@esbuild/android-arm64": "npm:0.21.5"
|
||||
"@esbuild/android-x64": "npm:0.21.5"
|
||||
"@esbuild/darwin-arm64": "npm:0.21.5"
|
||||
"@esbuild/darwin-x64": "npm:0.21.5"
|
||||
"@esbuild/freebsd-arm64": "npm:0.21.5"
|
||||
"@esbuild/freebsd-x64": "npm:0.21.5"
|
||||
"@esbuild/linux-arm": "npm:0.21.5"
|
||||
"@esbuild/linux-arm64": "npm:0.21.5"
|
||||
"@esbuild/linux-ia32": "npm:0.21.5"
|
||||
"@esbuild/linux-loong64": "npm:0.21.5"
|
||||
"@esbuild/linux-mips64el": "npm:0.21.5"
|
||||
"@esbuild/linux-ppc64": "npm:0.21.5"
|
||||
"@esbuild/linux-riscv64": "npm:0.21.5"
|
||||
"@esbuild/linux-s390x": "npm:0.21.5"
|
||||
"@esbuild/linux-x64": "npm:0.21.5"
|
||||
"@esbuild/netbsd-x64": "npm:0.21.5"
|
||||
"@esbuild/openbsd-x64": "npm:0.21.5"
|
||||
"@esbuild/sunos-x64": "npm:0.21.5"
|
||||
"@esbuild/win32-arm64": "npm:0.21.5"
|
||||
"@esbuild/win32-ia32": "npm:0.21.5"
|
||||
"@esbuild/win32-x64": "npm:0.21.5"
|
||||
dependenciesMeta:
|
||||
"@esbuild/aix-ppc64":
|
||||
optional: true
|
||||
"@esbuild/android-arm":
|
||||
optional: true
|
||||
"@esbuild/android-arm64":
|
||||
optional: true
|
||||
"@esbuild/android-x64":
|
||||
optional: true
|
||||
"@esbuild/darwin-arm64":
|
||||
optional: true
|
||||
"@esbuild/darwin-x64":
|
||||
optional: true
|
||||
"@esbuild/freebsd-arm64":
|
||||
optional: true
|
||||
"@esbuild/freebsd-x64":
|
||||
optional: true
|
||||
"@esbuild/linux-arm":
|
||||
optional: true
|
||||
"@esbuild/linux-arm64":
|
||||
optional: true
|
||||
"@esbuild/linux-ia32":
|
||||
optional: true
|
||||
"@esbuild/linux-loong64":
|
||||
optional: true
|
||||
"@esbuild/linux-mips64el":
|
||||
optional: true
|
||||
"@esbuild/linux-ppc64":
|
||||
optional: true
|
||||
"@esbuild/linux-riscv64":
|
||||
optional: true
|
||||
"@esbuild/linux-s390x":
|
||||
optional: true
|
||||
"@esbuild/linux-x64":
|
||||
optional: true
|
||||
"@esbuild/netbsd-x64":
|
||||
optional: true
|
||||
"@esbuild/openbsd-x64":
|
||||
optional: true
|
||||
"@esbuild/sunos-x64":
|
||||
optional: true
|
||||
"@esbuild/win32-arm64":
|
||||
optional: true
|
||||
"@esbuild/win32-ia32":
|
||||
optional: true
|
||||
"@esbuild/win32-x64":
|
||||
optional: true
|
||||
bin:
|
||||
esbuild: bin/esbuild
|
||||
checksum: 10c1/1bed0f5871043244bc2033f323a7e86e06d808df955b47bc5579bb3855d107b67f0adace7d4c747deea856cfc7bb798c3320a4b96ebb832112c915f377b5c9ed
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"escalade@npm:^3.1.1, escalade@npm:^3.2.0":
|
||||
version: 3.2.0
|
||||
resolution: "escalade@npm:3.2.0"
|
||||
|
|
@ -7967,6 +8223,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-tsconfig@npm:^4.7.0":
|
||||
version: 4.8.1
|
||||
resolution: "get-tsconfig@npm:4.8.1"
|
||||
dependencies:
|
||||
resolve-pkg-maps: "npm:^1.0.0"
|
||||
checksum: 10c1/680263b7ee8ceb66e88d6625d5b62fe432e280cf313e962e52bfaaae65d286ffe8f22ee35032a40ba0f9988469bcac92b1146ac837da33b8fad97a5b5b763806
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-user-locale@npm:^2.2.1":
|
||||
version: 2.3.2
|
||||
resolution: "get-user-locale@npm:2.3.2"
|
||||
|
|
@ -8058,7 +8323,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"glob@npm:^8.0.1":
|
||||
"glob@npm:^8.0.1, glob@npm:^8.0.3":
|
||||
version: 8.1.0
|
||||
resolution: "glob@npm:8.1.0"
|
||||
dependencies:
|
||||
|
|
@ -10198,7 +10463,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"loader-utils@npm:^2.0.0":
|
||||
"loader-utils@npm:^2.0.0, loader-utils@npm:^2.0.4":
|
||||
version: 2.0.4
|
||||
resolution: "loader-utils@npm:2.0.4"
|
||||
dependencies:
|
||||
|
|
@ -11308,7 +11573,7 @@ __metadata:
|
|||
"@floating-ui/react-dom-interactions": "npm:^0.10.3"
|
||||
"@jest/globals": "npm:^29.7.0"
|
||||
"@medv/finder": "npm:^3.1.0"
|
||||
"@openreplay/sourcemap-uploader": "npm:^3.0.8"
|
||||
"@openreplay/sourcemap-uploader": "npm:^3.0.10"
|
||||
"@sentry/browser": "npm:^5.21.1"
|
||||
"@svg-maps/world": "npm:^1.0.1"
|
||||
"@tanstack/react-query": "npm:^5.56.2"
|
||||
|
|
@ -11342,6 +11607,7 @@ __metadata:
|
|||
cypress: "npm:^13.3.0"
|
||||
cypress-image-snapshot: "npm:^4.0.1"
|
||||
dotenv: "npm:^6.2.0"
|
||||
esbuild-loader: "npm:^4.2.2"
|
||||
eslint: "npm:^8.15.0"
|
||||
eslint-plugin-react: "npm:^7.29.4"
|
||||
fflate: "npm:^0.8.2"
|
||||
|
|
@ -13847,6 +14113,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"resolve-pkg-maps@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "resolve-pkg-maps@npm:1.0.0"
|
||||
checksum: 10c1/e6f8dbe20ebbfdea61503670e5f325782e6d983e59e33c81b314a48910f2edcb9534975c5a14d789d2830c3ab3ae49f022dd6e2fdb56330f242ee3fbd60b46c5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"resolve.exports@npm:^2.0.0":
|
||||
version: 2.0.2
|
||||
resolution: "resolve.exports@npm:2.0.2"
|
||||
|
|
@ -14514,6 +14787,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"source-list-map@npm:^2.0.0":
|
||||
version: 2.0.1
|
||||
resolution: "source-list-map@npm:2.0.1"
|
||||
checksum: 10c1/bb3b0d59f90518f20b996eb57a29a62fbd0735f84649f2949591f01ef43a31a27b095a4c9b2f27edce4ea4c8d5b757b86476c532c813e1de84c340d7f2a41264
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.0.1, source-map-js@npm:^1.2.1":
|
||||
version: 1.2.1
|
||||
resolution: "source-map-js@npm:1.2.1"
|
||||
|
|
@ -16154,6 +16434,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"webpack-sources@npm:^1.4.3":
|
||||
version: 1.4.3
|
||||
resolution: "webpack-sources@npm:1.4.3"
|
||||
dependencies:
|
||||
source-list-map: "npm:^2.0.0"
|
||||
source-map: "npm:~0.6.1"
|
||||
checksum: 10c1/fc3c601c48df84178b6e8a297b3d844ea5580011b8cd7d382ffe0241b9fae1f44124337a2981d55f314cd4517f25d9fda20549cd96b279b47a00ac0727cea80f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"webpack-sources@npm:^3.2.3":
|
||||
version: 3.2.3
|
||||
resolution: "webpack-sources@npm:3.2.3"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ COMMON_PG_PASSWORD="change_me_pg_password"
|
|||
COMMON_VERSION="v1.16.0"
|
||||
## DB versions
|
||||
######################################
|
||||
POSTGRES_VERSION="14.5.0"
|
||||
POSTGRES_VERSION="15.10.0"
|
||||
REDIS_VERSION="6.0.12-debian-10-r33"
|
||||
MINIO_VERSION="2023.2.10-debian-11-r1"
|
||||
######################################
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ services:
|
|||
image: bitnami/redis:${REDIS_VERSION}
|
||||
container_name: redis
|
||||
volumes:
|
||||
- redisdata:/var/lib/postgresql/data
|
||||
- redisdata:/bitnami/redis/data
|
||||
networks:
|
||||
- openreplay-net
|
||||
environment:
|
||||
|
|
|
|||
|
|
@ -18,4 +18,4 @@ version: 0.1.1
|
|||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
AppVersion: "v1.21.0"
|
||||
AppVersion: "v1.21.1"
|
||||
|
|
|
|||
|
|
@ -18,4 +18,4 @@ version: 0.1.7
|
|||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
AppVersion: "v1.21.0"
|
||||
AppVersion: "v1.21.7"
|
||||
|
|
|
|||
|
|
@ -18,4 +18,4 @@ version: 0.1.10
|
|||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
AppVersion: "v1.21.0"
|
||||
AppVersion: "v1.21.16"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue