mirror of
https://github.com/TecharoHQ/anubis.git
synced 2026-04-05 16:28:17 +00:00
Compare commits
11 Commits
Xe/embed-s
...
Xe/decayma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0067659ac | ||
|
|
406732fe7e | ||
|
|
f79d36d21e | ||
|
|
f5b5243b5e | ||
|
|
2011b83a44 | ||
|
|
8ed89a6c6e | ||
|
|
9430d0e6a5 | ||
|
|
8b9dafac51 | ||
|
|
9997130a7c | ||
|
|
e239083944 | ||
|
|
abf6c8de57 |
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -9,3 +9,4 @@ Checklist:
|
||||
- [ ] Added a description of the changes to the `[Unreleased]` section of docs/docs/CHANGELOG.md
|
||||
- [ ] Added test cases to [the relevant parts of the codebase](https://anubis.techaro.lol/docs/developer/code-quality)
|
||||
- [ ] Ran integration tests `npm run test:integration` (unsupported on Windows, please use WSL)
|
||||
- [ ] All of my commits have [verified signatures](https://anubis.techaro.lol/docs/developer/signed-commits)
|
||||
|
||||
2
.github/actions/spelling/expect.txt
vendored
2
.github/actions/spelling/expect.txt
vendored
@@ -214,6 +214,7 @@ nicksnyder
|
||||
nobots
|
||||
NONINFRINGEMENT
|
||||
nosleep
|
||||
nullglob
|
||||
OCOB
|
||||
ogtag
|
||||
oklch
|
||||
@@ -278,6 +279,7 @@ Seo
|
||||
setsebool
|
||||
shellcheck
|
||||
shirou
|
||||
shopt
|
||||
Sidetrade
|
||||
simprint
|
||||
sitemap
|
||||
|
||||
4
.github/workflows/smoke-tests.yml
vendored
4
.github/workflows/smoke-tests.yml
vendored
@@ -28,11 +28,11 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||
with:
|
||||
node-version: latest
|
||||
|
||||
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version: stable
|
||||
|
||||
|
||||
2
.github/workflows/ssh-ci.yml
vendored
2
.github/workflows/ssh-ci.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
name: id_rsa
|
||||
known_hosts: ${{ secrets.CI_SSH_KNOWN_HOSTS }}
|
||||
|
||||
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version: stable
|
||||
|
||||
|
||||
4
.github/workflows/zizmor.yml
vendored
4
.github/workflows/zizmor.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@4959332f0f014c5280e7eac8b70c90cb574c9f9b # v6.6.0
|
||||
uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
||||
|
||||
- name: Run zizmor 🌈
|
||||
run: uvx zizmor --format sarif . > results.sarif
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
|
||||
uses: github/codeql-action/upload-sarif@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3.30.1
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: zizmor
|
||||
|
||||
@@ -51,6 +51,7 @@ var (
|
||||
cookieExpiration = flag.Duration("cookie-expiration-time", anubis.CookieDefaultExpirationTime, "The amount of time the authorization cookie is valid for")
|
||||
cookiePrefix = flag.String("cookie-prefix", anubis.CookieName, "prefix for browser cookies created by Anubis")
|
||||
cookiePartitioned = flag.Bool("cookie-partitioned", false, "if true, sets the partitioned flag on Anubis cookies, enabling CHIPS support")
|
||||
difficultyInJWT = flag.Bool("difficulty-in-jwt", false, "if true, adds a difficulty field in the JWT claims")
|
||||
useSimplifiedExplanation = flag.Bool("use-simplified-explanation", false, "if true, replaces the text when clicking \"Why am I seeing this?\" with a more simplified text for a non-tech-savvy audience.")
|
||||
forcedLanguage = flag.String("forced-language", "", "if set, this language is being used instead of the one from the request's Accept-Language header")
|
||||
hs512Secret = flag.String("hs512-secret", "", "secret used to sign JWTs, uses ed25519 if not set")
|
||||
@@ -317,6 +318,16 @@ func main() {
|
||||
log.Fatalf("can't parse policy file: %v", err)
|
||||
}
|
||||
|
||||
// Warn if persistent storage is used without a configured signing key
|
||||
if policy.Store.IsPersistent() {
|
||||
if *hs512Secret == "" && *ed25519PrivateKeyHex == "" && *ed25519PrivateKeyHexFile == "" {
|
||||
slog.Warn("[misconfiguration] persistent storage backend is configured, but no private key is set. " +
|
||||
"Challenges will be invalidated when Anubis restarts. " +
|
||||
"Set HS512_SECRET, ED25519_PRIVATE_KEY_HEX, or ED25519_PRIVATE_KEY_HEX_FILE to ensure challenges survive service restarts. " +
|
||||
"See: https://anubis.techaro.lol/docs/admin/installation#key-generation")
|
||||
}
|
||||
}
|
||||
|
||||
ruleErrorIDs := make(map[string]string)
|
||||
for _, rule := range policy.Bots {
|
||||
if rule.Action != config.RuleDeny {
|
||||
@@ -423,6 +434,7 @@ func main() {
|
||||
CookieSecure: *cookieSecure,
|
||||
PublicUrl: *publicUrl,
|
||||
JWTRestrictionHeader: *jwtRestrictionHeader,
|
||||
DifficultyInJWT: *difficultyInJWT,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("can't construct libanubis.Server: %v", err)
|
||||
|
||||
@@ -46,6 +46,11 @@ func main() {
|
||||
)
|
||||
}
|
||||
|
||||
if strings.Contains(*dockerTags, ",") {
|
||||
newTags := strings.Join(strings.Split(*dockerTags, ","), "\n")
|
||||
dockerTags = &newTags
|
||||
}
|
||||
|
||||
setOutput("docker_image", strings.SplitN(*dockerTags, "\n", 2)[0])
|
||||
|
||||
version, err := run("git describe --tags --always --dirty")
|
||||
|
||||
@@ -3,6 +3,6 @@ package data
|
||||
import "embed"
|
||||
|
||||
var (
|
||||
//go:embed botPolicies.yaml all:apps all:bots all:clients all:common all:crawlers all:meta all:services
|
||||
//go:embed botPolicies.yaml all:apps all:bots all:clients all:common all:crawlers all:meta
|
||||
BotPolicies embed.FS
|
||||
)
|
||||
|
||||
@@ -14,6 +14,12 @@ func Zilch[T any]() T {
|
||||
type Impl[K comparable, V any] struct {
|
||||
data map[K]decayMapEntry[V]
|
||||
lock sync.RWMutex
|
||||
|
||||
// deleteCh receives decay-deletion requests from readers.
|
||||
deleteCh chan deleteReq[K]
|
||||
// stopCh stops the background cleanup worker.
|
||||
stopCh chan struct{}
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
type decayMapEntry[V any] struct {
|
||||
@@ -21,30 +27,38 @@ type decayMapEntry[V any] struct {
|
||||
expiry time.Time
|
||||
}
|
||||
|
||||
// deleteReq is a request to remove a key if its expiry timestamp still matches
|
||||
// the observed one. This prevents racing with concurrent Set updates.
|
||||
type deleteReq[K comparable] struct {
|
||||
key K
|
||||
expiry time.Time
|
||||
}
|
||||
|
||||
// New creates a new DecayMap of key type K and value type V.
|
||||
//
|
||||
// Key types must be comparable to work with maps.
|
||||
func New[K comparable, V any]() *Impl[K, V] {
|
||||
return &Impl[K, V]{
|
||||
data: make(map[K]decayMapEntry[V]),
|
||||
m := &Impl[K, V]{
|
||||
data: make(map[K]decayMapEntry[V]),
|
||||
deleteCh: make(chan deleteReq[K], 1024),
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
m.wg.Add(1)
|
||||
go m.cleanupWorker()
|
||||
return m
|
||||
}
|
||||
|
||||
// expire forcibly expires a key by setting its time-to-live one second in the past.
|
||||
func (m *Impl[K, V]) expire(key K) bool {
|
||||
m.lock.RLock()
|
||||
// Use a single write lock to avoid RUnlock->Lock convoy.
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
val, ok := m.data[key]
|
||||
m.lock.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
m.lock.Lock()
|
||||
val.expiry = time.Now().Add(-1 * time.Second)
|
||||
m.data[key] = val
|
||||
m.lock.Unlock()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -53,19 +67,14 @@ func (m *Impl[K, V]) expire(key K) bool {
|
||||
// If the value does not exist, return false. Return true after
|
||||
// deletion.
|
||||
func (m *Impl[K, V]) Delete(key K) bool {
|
||||
m.lock.RLock()
|
||||
_, ok := m.data[key]
|
||||
m.lock.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// Use a single write lock to avoid RUnlock->Lock convoy.
|
||||
m.lock.Lock()
|
||||
delete(m.data, key)
|
||||
m.lock.Unlock()
|
||||
|
||||
return true
|
||||
defer m.lock.Unlock()
|
||||
_, ok := m.data[key]
|
||||
if ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// Get gets a value from the DecayMap by key.
|
||||
@@ -81,13 +90,12 @@ func (m *Impl[K, V]) Get(key K) (V, bool) {
|
||||
}
|
||||
|
||||
if time.Now().After(value.expiry) {
|
||||
m.lock.Lock()
|
||||
// Since previously reading m.data[key], the value may have been updated.
|
||||
// Delete the entry only if the expiry time is still the same.
|
||||
if m.data[key].expiry.Equal(value.expiry) {
|
||||
delete(m.data, key)
|
||||
// Defer decay deletion to the background worker to avoid convoy.
|
||||
select {
|
||||
case m.deleteCh <- deleteReq[K]{key: key, expiry: value.expiry}:
|
||||
default:
|
||||
// Channel full: drop request; a future Cleanup() or Get will retry.
|
||||
}
|
||||
m.lock.Unlock()
|
||||
|
||||
return Zilch[V](), false
|
||||
}
|
||||
@@ -125,3 +133,64 @@ func (m *Impl[K, V]) Len() int {
|
||||
defer m.lock.RUnlock()
|
||||
return len(m.data)
|
||||
}
|
||||
|
||||
// Close stops the background cleanup worker. It's optional to call; maps live
|
||||
// for the process lifetime in many cases. Call in tests or when you know you no
|
||||
// longer need the map to avoid goroutine leaks.
|
||||
func (m *Impl[K, V]) Close() {
|
||||
close(m.stopCh)
|
||||
m.wg.Wait()
|
||||
}
|
||||
|
||||
// cleanupWorker batches decay deletions to minimize lock contention.
|
||||
func (m *Impl[K, V]) cleanupWorker() {
|
||||
defer m.wg.Done()
|
||||
batch := make([]deleteReq[K], 0, 64)
|
||||
ticker := time.NewTicker(10 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
|
||||
flush := func() {
|
||||
if len(batch) == 0 {
|
||||
return
|
||||
}
|
||||
m.applyDeletes(batch)
|
||||
// reset batch without reallocating
|
||||
batch = batch[:0]
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case req := <-m.deleteCh:
|
||||
batch = append(batch, req)
|
||||
case <-ticker.C:
|
||||
flush()
|
||||
case <-m.stopCh:
|
||||
// Drain any remaining requests then exit
|
||||
for {
|
||||
select {
|
||||
case req := <-m.deleteCh:
|
||||
batch = append(batch, req)
|
||||
default:
|
||||
flush()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Impl[K, V]) applyDeletes(batch []deleteReq[K]) {
|
||||
now := time.Now()
|
||||
m.lock.Lock()
|
||||
for _, req := range batch {
|
||||
entry, ok := m.data[req.key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
// Only delete if the expiry is unchanged and already past.
|
||||
if entry.expiry.Equal(req.expiry) && now.After(entry.expiry) {
|
||||
delete(m.data, req.key)
|
||||
}
|
||||
}
|
||||
m.lock.Unlock()
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
func TestImpl(t *testing.T) {
|
||||
dm := New[string, string]()
|
||||
t.Cleanup(dm.Close)
|
||||
|
||||
dm.Set("test", "hi", 5*time.Minute)
|
||||
|
||||
@@ -28,10 +29,24 @@ func TestImpl(t *testing.T) {
|
||||
if ok {
|
||||
t.Error("got value even though it was supposed to be expired")
|
||||
}
|
||||
|
||||
// Deletion of expired entries after Get is deferred to a background worker.
|
||||
// Assert it eventually disappears from the map.
|
||||
deadline := time.Now().Add(200 * time.Millisecond)
|
||||
for time.Now().Before(deadline) {
|
||||
if dm.Len() == 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
}
|
||||
if dm.Len() != 0 {
|
||||
t.Fatalf("expected background cleanup to remove expired key; len=%d", dm.Len())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanup(t *testing.T) {
|
||||
dm := New[string, string]()
|
||||
t.Cleanup(dm.Close)
|
||||
|
||||
dm.Set("test1", "hi1", 1*time.Second)
|
||||
dm.Set("test2", "hi2", 2*time.Second)
|
||||
|
||||
@@ -13,10 +13,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
<!-- This changes the project to: -->
|
||||
|
||||
- Fix lock convoy problem in decaymap ([#1103](https://github.com/TecharoHQ/anubis/issues/1103))
|
||||
- Document missing environment variables in installation guide: `SLOG_LEVEL`, `COOKIE_PREFIX`, `FORCED_LANGUAGE`, and `TARGET_DISABLE_KEEPALIVE` ([#1086](https://github.com/TecharoHQ/anubis/pull/1086))
|
||||
- Add validation warning when persistent storage is used without setting signing keys
|
||||
- Fixed `robots2policy` to properly group consecutive user agents into `any:` instead of only processing the last one ([#925](https://github.com/TecharoHQ/anubis/pull/925))
|
||||
- Add the [`s3api` storage backend](./admin/policies.mdx#s3api) to allow Anubis to use S3 API compatible object storage as its storage backend.
|
||||
- Add the `services` folder to the embedded data filesystem.
|
||||
- Make `cmd/containerbuild` support commas for separating elements of the `--docker-tags` argument as well as newlines.
|
||||
- Add the `DIFFICULTY_IN_JWT` option, which allows one to add the `difficulty` field in the JWT claims which indicates the difficulty of the token ([#1063](https://github.com/TecharoHQ/anubis/pull/1063))
|
||||
- Ported the client-side JS to TypeScript to avoid egregious errors in the future.
|
||||
- Fixes concurrency problems with very old browsers ([#1082](https://github.com/TecharoHQ/anubis/issues/1082)).
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ Currently the following settings are configurable via the policy file:
|
||||
Anubis uses these environment variables for configuration:
|
||||
|
||||
| Environment Variable | Default value | Explanation |
|
||||
|:-------------------------------|:------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| :----------------------------- | :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `BASE_PREFIX` | unset | If set, adds a global prefix to all Anubis endpoints (everything starting with `/.within.website/x/anubis/`). For example, setting this to `/myapp` would make Anubis accessible at `/myapp/` instead of `/`. This is useful when running Anubis behind a reverse proxy that routes based on path prefixes. |
|
||||
| `BIND` | `:8923` | The network address that Anubis listens on. For `unix`, set this to a path: `/run/anubis/instance.sock` |
|
||||
| `BIND_NETWORK` | `tcp` | The address family that Anubis listens on. Accepts `tcp`, `unix` and anything Go's [`net.Listen`](https://pkg.go.dev/net#Listen) supports. |
|
||||
@@ -70,8 +70,9 @@ Anubis uses these environment variables for configuration:
|
||||
| `COOKIE_PREFIX` | `anubis-cookie` | The prefix used for browser cookies created by Anubis. Useful for customization or avoiding conflicts with other applications. |
|
||||
| `COOKIE_SECURE` | `true` | If set to `true`, enables the [Secure flag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cookies#block_access_to_your_cookies), meaning that the cookies will only be transmitted over HTTPS. If Anubis is used in an unsecure context (plain HTTP), this will be need to be set to false |
|
||||
| `DIFFICULTY` | `4` | The difficulty of the challenge, or the number of leading zeroes that must be in successful responses. |
|
||||
| `ED25519_PRIVATE_KEY_HEX` | unset | The hex-encoded ed25519 private key used to sign Anubis responses. If this is not set, Anubis will generate one for you. This should be exactly 64 characters long. When running multiple instances on the same base domain, the key must be the same across all instances. See below for details. |
|
||||
| `ED25519_PRIVATE_KEY_HEX_FILE` | unset | Path to a file containing the hex-encoded ed25519 private key. Only one of this or its sister option may be set. |
|
||||
| `DIFFICULTY_IN_JWT` | `false` | If set to `true`, adds the `difficulty` field into JWT claims, which indicates the difficulty the token has been generated. This may be useful for statistics and debugging. |
|
||||
| `ED25519_PRIVATE_KEY_HEX` | unset | The hex-encoded ed25519 private key used to sign Anubis responses. If this is not set, Anubis will generate one for you. This should be exactly 64 characters long. **Required when using persistent storage backends** (like bbolt) to ensure challenges survive service restarts. When running multiple instances on the same base domain, the key must be the same across all instances. See below for details. |
|
||||
| `ED25519_PRIVATE_KEY_HEX_FILE` | unset | Path to a file containing the hex-encoded ed25519 private key. Only one of this or its sister option may be set. **Required when using persistent storage backends** (like bbolt) to ensure challenges survive service restarts. When running multiple instances on the same base domain, the key must be the same across all instances. |
|
||||
| `JWT_RESTRICTION_HEADER` | `X-Real-IP` | If set, the JWT is only valid if the current value of this header matches the value when the JWT was created. You can use it e.g. to restrict a JWT to the source IP of the user using `X-Real-IP`. |
|
||||
| `METRICS_BIND` | `:9090` | The network address that Anubis serves Prometheus metrics on. See `BIND` for more information. |
|
||||
| `METRICS_BIND_NETWORK` | `tcp` | The address family that the Anubis metrics server listens on. See `BIND_NETWORK` for more information. |
|
||||
@@ -100,14 +101,14 @@ If you don't know or understand what these settings mean, ignore them. These are
|
||||
|
||||
:::
|
||||
|
||||
| Environment Variable | Default value | Explanation |
|
||||
| :---------------------------- | :------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `FORCED_LANGUAGE` | unset | If set, forces Anubis to display challenge pages in the specified language instead of using the browser's Accept-Language header. Use ISO 639-1 language codes (e.g., `de` for German, `fr` for French). |
|
||||
| `HS512_SECRET` | unset | Secret string for JWT HS512 algorithm. If this is not set, Anubis will use ED25519 as defined via the variables above. The longer the better; 128 chars should suffice. |
|
||||
| `TARGET_DISABLE_KEEPALIVE` | `false` | If `true`, disables HTTP keep-alive for connections to the target backend. Useful for backends that don't handle keep-alive properly. |
|
||||
| `TARGET_HOST` | unset | If set, overrides the Host header in requests forwarded to `TARGET`. |
|
||||
| `TARGET_INSECURE_SKIP_VERIFY` | `false` | If `true`, skip TLS certificate validation for targets that listen over `https`. If your backend does not listen over `https`, ignore this setting. |
|
||||
| `TARGET_SNI` | unset | If set, overrides the TLS handshake hostname in requests forwarded to `TARGET`. |
|
||||
| Environment Variable | Default value | Explanation |
|
||||
| :---------------------------- | :------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `FORCED_LANGUAGE` | unset | If set, forces Anubis to display challenge pages in the specified language instead of using the browser's Accept-Language header. Use ISO 639-1 language codes (e.g., `de` for German, `fr` for French). |
|
||||
| `HS512_SECRET` | unset | Secret string for JWT HS512 algorithm. If this is not set, Anubis will use ED25519 as defined via the variables above. The longer the better; 128 chars should suffice. **Required when using persistent storage backends** (like bbolt) to ensure challenges survive service restarts. When running multiple instances on the same base domain, the key must be the same across all instances. |
|
||||
| `TARGET_DISABLE_KEEPALIVE` | `false` | If `true`, disables HTTP keep-alive for connections to the target backend. Useful for backends that don't handle keep-alive properly. |
|
||||
| `TARGET_HOST` | unset | If set, overrides the Host header in requests forwarded to `TARGET`. |
|
||||
| `TARGET_INSECURE_SKIP_VERIFY` | `false` | If `true`, skip TLS certificate validation for targets that listen over `https`. If your backend does not listen over `https`, ignore this setting. |
|
||||
| `TARGET_SNI` | unset | If set, overrides the TLS handshake hostname in requests forwarded to `TARGET`. |
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
677
docs/package-lock.json
generated
677
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -25,7 +25,7 @@
|
||||
"react-dom": "^19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "^3.8.1",
|
||||
"@docusaurus/module-type-aliases": "^3.0.1",
|
||||
"@docusaurus/tsconfig": "^3.8.1",
|
||||
"@docusaurus/types": "^3.8.1",
|
||||
"typescript": "~5.6.2"
|
||||
@@ -45,4 +45,4 @@
|
||||
"engines": {
|
||||
"node": ">=18.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,6 +501,12 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
|
||||
var tokenString string
|
||||
|
||||
// check if JWTRestrictionHeader is set and header is in request
|
||||
claims := jwt.MapClaims{
|
||||
"challenge": chall.ID,
|
||||
"method": rule.Challenge.Algorithm,
|
||||
"policyRule": rule.Hash(),
|
||||
"action": string(cr.Rule),
|
||||
}
|
||||
if s.opts.JWTRestrictionHeader != "" {
|
||||
if r.Header.Get(s.opts.JWTRestrictionHeader) == "" {
|
||||
lg.Error("JWTRestrictionHeader is set in config but not found in request, please check your reverse proxy config.")
|
||||
@@ -508,22 +514,13 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
|
||||
s.respondWithError(w, r, "failed to sign JWT")
|
||||
return
|
||||
} else {
|
||||
tokenString, err = s.signJWT(jwt.MapClaims{
|
||||
"challenge": chall.ID,
|
||||
"method": rule.Challenge.Algorithm,
|
||||
"policyRule": rule.Hash(),
|
||||
"action": string(cr.Rule),
|
||||
"restriction": internal.SHA256sum(r.Header.Get(s.opts.JWTRestrictionHeader)),
|
||||
})
|
||||
claims["restriction"] = internal.SHA256sum(r.Header.Get(s.opts.JWTRestrictionHeader))
|
||||
}
|
||||
} else {
|
||||
tokenString, err = s.signJWT(jwt.MapClaims{
|
||||
"challenge": chall.ID,
|
||||
"method": rule.Challenge.Algorithm,
|
||||
"policyRule": rule.Hash(),
|
||||
"action": string(cr.Rule),
|
||||
})
|
||||
}
|
||||
if s.opts.DifficultyInJWT {
|
||||
claims["difficulty"] = rule.Challenge.Difficulty
|
||||
}
|
||||
tokenString, err = s.signJWT(claims)
|
||||
|
||||
if err != nil {
|
||||
lg.Error("failed to sign JWT", "err", err)
|
||||
|
||||
4
lib/challenge/metarefresh/metarefresh_templ.go
generated
4
lib/challenge/metarefresh/metarefresh_templ.go
generated
@@ -93,9 +93,9 @@ func page(redir string, difficulty int, loc *localization.SimpleLocalizer) templ
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d; url=%s", difficulty, redir))
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d; url=%s", difficulty+1, redir))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `metarefresh.templ`, Line: 16, Col: 83}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `metarefresh.templ`, Line: 16, Col: 85}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
||||
@@ -40,9 +40,9 @@ for the JavaScript code in this page.
|
||||
|
||||
mkdir -p static/js
|
||||
|
||||
for file in js/*.jsx; do
|
||||
for file in js/*.tsx; do
|
||||
filename="${file##*/}" # Extracts "app.jsx" from "./js/app.jsx"
|
||||
output="${filename%.jsx}.js" # Changes "app.jsx" to "app.js"
|
||||
output="${filename%.tsx}.js" # Changes "app.jsx" to "app.js"
|
||||
echo $output
|
||||
|
||||
esbuild "${file}" --minify --bundle --outfile=static/"${output}" --banner:js="${LICENSE}"
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
import { render, h, Fragment } from 'preact';
|
||||
import { useState, useEffect } from 'preact/hooks';
|
||||
import { g, j, u, x } from "./xeact.js";
|
||||
import { Sha256 } from '@aws-crypto/sha256-js';
|
||||
|
||||
/** @jsx h */
|
||||
/** @jsxFrag Fragment */
|
||||
|
||||
function toHexString(arr) {
|
||||
return Array.from(arr)
|
||||
.map((c) => c.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
}
|
||||
|
||||
const App = () => {
|
||||
const [state, setState] = useState(null);
|
||||
const [imageURL, setImageURL] = useState(null);
|
||||
const [passed, setPassed] = useState(false);
|
||||
const [challenge, setChallenge] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
setState(j("preact_info"));
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setImageURL(state.pensive_url);
|
||||
const hash = new Sha256('');
|
||||
hash.update(state.challenge);
|
||||
setChallenge(toHexString(hash.digestSync()));
|
||||
}, [state]);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
setPassed(true);
|
||||
}, state.difficulty * 125);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [challenge]);
|
||||
|
||||
useEffect(() => {
|
||||
window.location.href = u(state.redir, {
|
||||
result: challenge,
|
||||
});
|
||||
}, [passed]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{imageURL !== null && (
|
||||
<img src={imageURL} style="width:100%;max-width:256px;" />
|
||||
)}
|
||||
{state !== null && (
|
||||
<>
|
||||
<p id="status">{state.loading_message}</p>
|
||||
<p>{state.connection_security_message}</p>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
x(g("app"));
|
||||
render(<App />, g("app"));
|
||||
87
lib/challenge/preact/js/app.tsx
Normal file
87
lib/challenge/preact/js/app.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import { render, h, Fragment } from "preact";
|
||||
import { useState, useEffect } from "preact/hooks";
|
||||
import { g, j, r, u, x } from "./xeact.js";
|
||||
import { Sha256 } from "@aws-crypto/sha256-js";
|
||||
|
||||
/** @jsx h */
|
||||
/** @jsxFrag Fragment */
|
||||
|
||||
function toHexString(arr: Uint8Array) {
|
||||
return Array.from(arr)
|
||||
.map((c) => c.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
}
|
||||
|
||||
interface PreactInfo {
|
||||
redir: string;
|
||||
challenge: string;
|
||||
difficulty: number;
|
||||
connection_security_message: string;
|
||||
loading_message: string;
|
||||
pensive_url: string;
|
||||
}
|
||||
|
||||
const App = () => {
|
||||
const [state, setState] = useState<PreactInfo>();
|
||||
const [imageURL, setImageURL] = useState<string | null>(null);
|
||||
const [passed, setPassed] = useState<boolean>(false);
|
||||
const [challenge, setChallenge] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setState(j("preact_info"));
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (state === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
setImageURL(state?.pensive_url);
|
||||
const hash = new Sha256("");
|
||||
hash.update(state.challenge);
|
||||
setChallenge(toHexString(hash.digestSync()));
|
||||
}, [state]);
|
||||
|
||||
useEffect(() => {
|
||||
if (state === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
setPassed(true);
|
||||
}, state?.difficulty * 125);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [challenge]);
|
||||
|
||||
useEffect(() => {
|
||||
if (state === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (challenge === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.href = u(state.redir, {
|
||||
result: challenge,
|
||||
});
|
||||
}, [passed]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{imageURL !== null && (
|
||||
<img src={imageURL} style={{ width: "100%", maxWidth: "256px" }} />
|
||||
)}
|
||||
{state !== undefined && (
|
||||
<>
|
||||
<p id="status">{state.loading_message}</p>
|
||||
<p>{state.connection_security_message}</p>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
x(g("app"));
|
||||
render(<App />, g("app"));
|
||||
@@ -46,6 +46,7 @@ type Options struct {
|
||||
Logger *slog.Logger
|
||||
PublicUrl string
|
||||
JWTRestrictionHeader string
|
||||
DifficultyInJWT bool
|
||||
}
|
||||
|
||||
func LoadPoliciesOrDefault(ctx context.Context, fname string, defaultDifficulty int) (*policy.ParsedConfig, error) {
|
||||
|
||||
@@ -62,5 +62,6 @@
|
||||
"js_iterations": "iteracijų",
|
||||
"js_finished_reading": "Viską perskaičiau, tęskime →",
|
||||
"js_calculation_error": "Skaičiavimo klaida!",
|
||||
"js_calculation_error_msg": "Nepavyko įveikti iššūkio:"
|
||||
"js_calculation_error_msg": "Nepavyko įveikti iššūkio:",
|
||||
"missing_required_forwarded_headers": "Trūksta privalomų X-Forwarded-* antraščių"
|
||||
}
|
||||
|
||||
@@ -11,10 +11,9 @@ import (
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
// Sentinel error values used for testing and in admin-visible error messages.
|
||||
// Sentinel error value used for testing and in admin-visible error messages.
|
||||
var (
|
||||
ErrBucketDoesNotExist = errors.New("bbolt: bucket does not exist")
|
||||
ErrNotExists = errors.New("bbolt: value does not exist in store")
|
||||
ErrNotExists = errors.New("bbolt: value does not exist in store")
|
||||
)
|
||||
|
||||
// Store implements store.Interface backed by bbolt[1].
|
||||
@@ -150,6 +149,10 @@ func (s *Store) cleanup(ctx context.Context) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Store) IsPersistent() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Store) cleanupThread(ctx context.Context) {
|
||||
t := time.NewTicker(time.Hour)
|
||||
defer t.Stop()
|
||||
|
||||
@@ -37,6 +37,11 @@ type Interface interface {
|
||||
|
||||
// Set puts a value into the store that expires according to its expiry.
|
||||
Set(ctx context.Context, key string, value []byte, expiry time.Duration) error
|
||||
|
||||
// IsPersistent returns true if this storage backend persists data across
|
||||
// service restarts (e.g., bbolt, valkey). Returns false for volatile storage
|
||||
// like in-memory backends.
|
||||
IsPersistent() bool
|
||||
}
|
||||
|
||||
func z[T any]() T { return *new(T) }
|
||||
@@ -88,3 +93,7 @@ func (j *JSON[T]) Set(ctx context.Context, key string, value T, expiry time.Dura
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *JSON[T]) IsPersistent() bool {
|
||||
return j.Underlying.IsPersistent()
|
||||
}
|
||||
|
||||
@@ -48,6 +48,10 @@ func (i *impl) Set(_ context.Context, key string, value []byte, expiry time.Dura
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *impl) IsPersistent() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (i *impl) cleanupThread(ctx context.Context) {
|
||||
t := time.NewTicker(5 * time.Minute)
|
||||
defer t.Stop()
|
||||
|
||||
@@ -47,3 +47,7 @@ func (s *Store) Set(ctx context.Context, key string, value []byte, expiry time.D
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) IsPersistent() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
710
package-lock.json
generated
710
package-lock.json
generated
@@ -19,7 +19,7 @@
|
||||
"playwright": "^1.52.0",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"postcss-import": "^16.1.1",
|
||||
"postcss-import-url": "^7.2.0",
|
||||
"postcss-import-url": "^1.0.0",
|
||||
"postcss-url": "^10.1.3"
|
||||
}
|
||||
},
|
||||
@@ -553,6 +553,13 @@
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
@@ -651,6 +658,13 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/bluebird": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/boolbase": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||
@@ -774,6 +788,18 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/cli": {
|
||||
"version": "0.4.4-2",
|
||||
"resolved": "https://registry.npmjs.org/cli/-/cli-0.4.4-2.tgz",
|
||||
"integrity": "sha512-zvFHTz+T8S4gejPHNVtdqc0mDnWmZcwd5juDF4ScZkPerNdl/9aiWcBv3l57v81jzq+n89eYLkRJdvc5aWJROA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob": ">= 3.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.2.5"
|
||||
}
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
@@ -833,6 +859,17 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/config-chain": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
|
||||
"integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ini": "^1.3.4",
|
||||
"proto-list": "~1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/css-declaration-sorter": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz",
|
||||
@@ -1047,6 +1084,23 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/deep-equal": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.1.2.tgz",
|
||||
"integrity": "sha512-rUCt39nKM7s6qUyYgp/reJmtXjgkOS/JbLO24DioMZaBNkD3b7C7cD3zJjSyjclEElNTpetAIRD6fMIbBIbX1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dependency-graph": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz",
|
||||
@@ -1057,6 +1111,15 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz",
|
||||
"integrity": "sha512-0bTLzyr1S59cPsgAD/lR+ivvHTbgPb+k/mUR6WGqma1J6QDU+kUegI8uQFuH/cMUNK7JGN3Tk1Y5Jf2MO85WrA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-serializer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||
@@ -1195,6 +1258,16 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
@@ -1222,10 +1295,19 @@
|
||||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.1.0.tgz",
|
||||
"integrity": "sha512-ROG9M8tikYOuOJsvRBggh10WiQ/JebnldAwuCaQyFoiAUIE9XrYVnpznIjOQGZfCMzxzEBYHQr/LHJp3tcndzQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "11.3.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
|
||||
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
|
||||
"version": "11.3.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz",
|
||||
"integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1237,6 +1319,13 @@
|
||||
"node": ">=14.14"
|
||||
}
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
@@ -1272,6 +1361,28 @@
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
@@ -1285,6 +1396,19 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
@@ -1292,6 +1416,35 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/growl": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz",
|
||||
"integrity": "sha512-VWv7s1EI41AG2LiCr7uAuxWikLDN1SQOuEUc37d/P34NAIIYgkvWYngNw0d9d9iCrDFL0SYCE9UQpxhIjjtuLg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/has-ansi": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
|
||||
"integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/has-ansi/node_modules/ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
@@ -1312,6 +1465,32 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ini": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
@@ -1391,10 +1570,68 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jade": {
|
||||
"version": "0.26.3",
|
||||
"resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz",
|
||||
"integrity": "sha512-mkk3vzUHFjzKjpCXeu+IjXeZD+QOTjUUdubgmHtHTDwvAO2ZTkMTTVrapts5CWz3JvJryh/4KWZpjeZrCepZ3A==",
|
||||
"deprecated": "Jade has been renamed to pug, please install the latest version of pug instead of jade",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"commander": "0.6.1",
|
||||
"mkdirp": "0.3.0"
|
||||
},
|
||||
"bin": {
|
||||
"jade": "bin/jade"
|
||||
}
|
||||
},
|
||||
"node_modules/jade/node_modules/commander": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz",
|
||||
"integrity": "sha512-0fLycpl1UMTGX257hRsu/arL/cUbcvQM4zMKwvLvzXtfdezIV4yotPS2dYtknF+NmEfWSoCEF6+hj9XLm/6hEw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4.x"
|
||||
}
|
||||
},
|
||||
"node_modules/jade/node_modules/mkdirp": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz",
|
||||
"integrity": "sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew==",
|
||||
"deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)",
|
||||
"dev": true,
|
||||
"license": "MIT/X11",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/js-base64": {
|
||||
"version": "2.6.4",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz",
|
||||
"integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/js-beautify": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.4.2.tgz",
|
||||
"integrity": "sha512-0o7oku1AcG66QoDIoSLCBENbyFgV6WHoqnZhC8oL4URTWYDzIXWo3tTGTLrLh6jR91miKS5YC+WBZeYC5iZMQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"config-chain": "~1.1.5",
|
||||
"mkdirp": "0.3.5",
|
||||
"nopt": "~2.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"css-beautify": "js/bin/css-beautify.js",
|
||||
"html-beautify": "js/bin/html-beautify.js",
|
||||
"js-beautify": "js/bin/js-beautify.js"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonfile": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
|
||||
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1417,13 +1654,6 @@
|
||||
"url": "https://github.com/sponsors/antonk52"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash.assign": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
|
||||
"integrity": "sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||
@@ -1431,13 +1661,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.trim": {
|
||||
"version": "4.5.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz",
|
||||
"integrity": "sha512-nJAlRl/K+eiOehWKDzoBVrSMhK0K3A3YQsUNXHQa5yIrKBAhsZgSu3KoAFoFT+mEgiyBHddZ0pRk1ITpIp90Wg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.uniq": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
|
||||
@@ -1445,6 +1668,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "2.7.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
|
||||
"integrity": "sha512-WpibWJ60c3AgAz8a2iYErDrcT2C7OmKnsWhIcHOjkUHFjkXncJhtLxNSqUmxRxRunpb5I8Vprd7aNSd2NtksJQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/make-dir": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||
@@ -1494,6 +1724,95 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz",
|
||||
"integrity": "sha512-8OCq0De/h9ZxseqzCH8Kw/Filf5pF/vMI6+BH7Lu0jXz2pqYCjTAQRolSxRIi+Ax+oCCjlxoJMP0YQ4XlrQNHg==",
|
||||
"deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mocha": {
|
||||
"version": "1.17.0",
|
||||
"resolved": "https://registry.npmjs.org/mocha/-/mocha-1.17.0.tgz",
|
||||
"integrity": "sha512-Bmjo5ZIr+RcxCKRLFpE7tpGiYemqCkWNVBx31seyUv+c45MahZcBBcoRN33yMhvOBmiq0ABhpENk19WtM3BcOw==",
|
||||
"deprecated": "Mocha v1.x is no longer supported.",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"commander": "2.0.0",
|
||||
"debug": "*",
|
||||
"diff": "1.0.7",
|
||||
"glob": "3.2.3",
|
||||
"growl": "1.7.x",
|
||||
"jade": "0.26.3",
|
||||
"mkdirp": "0.3.5"
|
||||
},
|
||||
"bin": {
|
||||
"_mocha": "bin/_mocha",
|
||||
"mocha": "bin/mocha"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.x"
|
||||
}
|
||||
},
|
||||
"node_modules/mocha/node_modules/commander": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz",
|
||||
"integrity": "sha512-qebjpyeaA/nJ4w3EO2cV2++/zEkccPnjWogzA2rff+Lk8ILI75vULeTmyd4wPxWdKwtP3J+G39IXVZadh0UHyw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6.x"
|
||||
}
|
||||
},
|
||||
"node_modules/mocha/node_modules/glob": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz",
|
||||
"integrity": "sha512-WPaLsMHD1lYEqAmIQI6VOJSPwuBdGShDWnj1yUo0vQqEO809R8W3LM9OVU13CnnDhyv/EiNwOtxEW74SmrzS6w==",
|
||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||
"dev": true,
|
||||
"license": "BSD",
|
||||
"dependencies": {
|
||||
"graceful-fs": "~2.0.0",
|
||||
"inherits": "2",
|
||||
"minimatch": "~0.2.11"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/mocha/node_modules/graceful-fs": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz",
|
||||
"integrity": "sha512-hcj/NTUWv+C3MbqrVb9F+aH6lvTwEHJdx2foBxlrVq5h6zE8Bfu4pv4CAAqbDcZrw/9Ak5lsRXlY9Ao8/F0Tuw==",
|
||||
"deprecated": "please upgrade to graceful-fs 4 for compatibility with current and future versions of Node.js",
|
||||
"dev": true,
|
||||
"license": "BSD",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mocha/node_modules/minimatch": {
|
||||
"version": "0.2.14",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
|
||||
"integrity": "sha512-zZ+Jy8lVWlvqqeM8iZB7w7KmQkoJn8djM585z88rywrEbzoqawVa9FR5p2hwD+y74nfuKOjmNvi9gtWJNLqHvA==",
|
||||
"deprecated": "Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lru-cache": "2",
|
||||
"sigmund": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
@@ -1521,6 +1840,19 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nopt": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.2.tgz",
|
||||
"integrity": "sha512-x8vXm7BZ2jE1Txrxh/hO74HTuYZQEbo8edoRcANgdZ4+PCV+pbjd/xdummkmjjC7LU5EjPzlu8zEq/oxWylnKA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"abbrev": "1"
|
||||
},
|
||||
"bin": {
|
||||
"nopt": "bin/nopt.js"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
@@ -1554,6 +1886,26 @@
|
||||
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
@@ -1561,6 +1913,83 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/phpfn": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/phpfn/-/phpfn-1.0.0.tgz",
|
||||
"integrity": "sha512-hwl2fXpXDOHCOlFDkNYXwD4FUKsddgXooF7Cb8eyynt82Ej9DLVfL6P/2d6L0uQghJq1X6DUnTd0rKM3yC8oOw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"phpjs": "latest"
|
||||
}
|
||||
},
|
||||
"node_modules/phpjs": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/phpjs/-/phpjs-1.3.2.tgz",
|
||||
"integrity": "sha512-S/V298ABWBDLsWgssVl91JmexMvTmmBR4oufeHvQU3W63+xOBluVtbVEoMyxv6ZdFuj/fx6BXe/WC6gWnO+lig==",
|
||||
"deprecated": "phpjs is no longer maintained. Please use Locutus instead: https://locutus.io",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cli": "0.4.4-2",
|
||||
"deep-equal": "0.1.2",
|
||||
"glob": "3.2.1",
|
||||
"js-beautify": "1.4.2",
|
||||
"mocha": "1.17.0",
|
||||
"send": "0.1.0",
|
||||
"underscore": "1.5.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/phpjs/node_modules/glob": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-3.2.1.tgz",
|
||||
"integrity": "sha512-wvxQZUqjkvW//FJMr/DCmPlAOFcrmf2ojnUddQTdgAQ5XkKL8ILfob0Rz+Ch/fSiols6EtiHRJS3i9W0kBRZmQ==",
|
||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||
"dev": true,
|
||||
"license": "BSD",
|
||||
"dependencies": {
|
||||
"graceful-fs": "~1.2.0",
|
||||
"inherits": "1",
|
||||
"minimatch": "~0.2.11"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/phpjs/node_modules/graceful-fs": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz",
|
||||
"integrity": "sha512-iiTUZ5vZ+2ZV+h71XAgwCSu6+NAizhFU3Yw8aC/hH5SQ3SnISqEqAek40imAFGtDcwJKNhXvSY+hzIolnLwcdQ==",
|
||||
"deprecated": "please upgrade to graceful-fs 4 for compatibility with current and future versions of Node.js",
|
||||
"dev": true,
|
||||
"license": "BSD",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/phpjs/node_modules/inherits": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz",
|
||||
"integrity": "sha512-Al67oatbRSo3RV5hRqIoln6Y5yMVbJSIn4jEJNL7VCImzq/kLr7vvb6sFRJXqr8rpHc/2kJOM+y0sPKN47VdzA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/phpjs/node_modules/minimatch": {
|
||||
"version": "0.2.14",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
|
||||
"integrity": "sha512-zZ+Jy8lVWlvqqeM8iZB7w7KmQkoJn8djM585z88rywrEbzoqawVa9FR5p2hwD+y74nfuKOjmNvi9gtWJNLqHvA==",
|
||||
"deprecated": "Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lru-cache": "2",
|
||||
"sigmund": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
@@ -1825,23 +2254,116 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-import-url": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-import-url/-/postcss-import-url-7.2.0.tgz",
|
||||
"integrity": "sha512-El61K/5+Rv753G9mBiHyQlOIN2mBfN0YHPMXLlgIo/m1+tPDLM32wd97WoUjc8FHUnC6EyyfVA8RDuKoyuVl0Q==",
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-import-url/-/postcss-import-url-1.0.0.tgz",
|
||||
"integrity": "sha512-sXZVBws7VJZDc3P60oTI/7hR5I5EZnjIrmm9QFQY6iwhdmRHi4o9deYoAcnV6jaKrPzzaqO8VGrxf6X2yxUfHQ==",
|
||||
"dev": true,
|
||||
"license": "Beerware",
|
||||
"dependencies": {
|
||||
"bluebird": "^3.0.2",
|
||||
"http-https": "^1.0.0",
|
||||
"is-url": "^1.2.1",
|
||||
"phpfn": "^1.0.0",
|
||||
"postcss": "^5.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-import-url/node_modules/ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-import-url/node_modules/ansi-styles": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
|
||||
"integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-import-url/node_modules/chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"http-https": "^1.0.0",
|
||||
"is-url": "^1.2.4",
|
||||
"lodash.assign": "^4.2.0",
|
||||
"lodash.trim": "^4.5.1",
|
||||
"resolve-relative-url": "^1.0.0"
|
||||
"ansi-styles": "^2.2.1",
|
||||
"escape-string-regexp": "^1.0.2",
|
||||
"has-ansi": "^2.0.0",
|
||||
"strip-ansi": "^3.0.0",
|
||||
"supports-color": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-import-url/node_modules/chalk/node_modules/supports-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
"integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-import-url/node_modules/has-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-import-url/node_modules/postcss": {
|
||||
"version": "5.2.18",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
|
||||
"integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^1.1.3",
|
||||
"js-base64": "^2.1.9",
|
||||
"source-map": "^0.5.6",
|
||||
"supports-color": "^3.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.0.0"
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-import-url/node_modules/strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-import-url/node_modules/supports-color": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
|
||||
"integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-load-config": {
|
||||
@@ -2347,21 +2869,20 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
||||
"integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==",
|
||||
"node_modules/proto-list": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
|
||||
"integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/querystring": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
||||
"integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==",
|
||||
"deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
|
||||
"node_modules/range-parser": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz",
|
||||
"integrity": "sha512-okJVEq9DbZyg+5lD8pr6ooQmeA0uu8DYIyAU7VK1WUUK7hctI1yw2ZHhKiKjB6RXaDrYRmTR4SsIHkyiQpaLMA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.4.x"
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
@@ -2418,16 +2939,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-relative-url": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-relative-url/-/resolve-relative-url-1.0.0.tgz",
|
||||
"integrity": "sha512-zpcelQBAmrwckiyRmym9os1goECU3EzuTU/UrYkGzXV0i14n8FkyGUvwkOYA5klqVLq1Hz/EiFZMS7bZQdd+EA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"url": "0.10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
|
||||
@@ -2445,6 +2956,34 @@
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.1.0.tgz",
|
||||
"integrity": "sha512-D/GaJQQYx7ICNq9Te5V4wHetfDQdFk3HJ4oBfDUBNW7XQmLbJ8sQDm/wFvVUUpKN8tluOnO1dFdM8KODn6D79w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"debug": "*",
|
||||
"fresh": "0.1.0",
|
||||
"mime": "1.2.6",
|
||||
"range-parser": "0.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/mime": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.2.6.tgz",
|
||||
"integrity": "sha512-S4yfg1ehMduQ5F3NeTUUWJesnut4RvymaRSatO4etOm68yZE98oCg2GtgG0coGYx03GCv240sezMvRwFk8DUKw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/sigmund": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
|
||||
"integrity": "sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/slash": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
|
||||
@@ -2458,6 +2997,16 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
@@ -2560,14 +3109,14 @@
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.14",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
|
||||
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
|
||||
"version": "0.2.15",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
||||
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.4.4",
|
||||
"picomatch": "^4.0.2"
|
||||
"fdir": "^6.5.0",
|
||||
"picomatch": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
@@ -2577,11 +3126,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby/node_modules/fdir": {
|
||||
"version": "6.4.6",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
|
||||
"integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
},
|
||||
@@ -2592,9 +3144,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby/node_modules/picomatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2623,6 +3175,12 @@
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/underscore": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.5.2.tgz",
|
||||
"integrity": "sha512-yejOFsRnTJs0N9CK5Apzf6maDO2djxGoLLrlZlvGs2o9ZQuhIhDL18rtFyy4FBIbOkzA6+4hDgXbgz5EvDQCXQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||
@@ -2664,17 +3222,6 @@
|
||||
"browserslist": ">= 4.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url": {
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
|
||||
"integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"punycode": "1.3.2",
|
||||
"querystring": "0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
@@ -2700,6 +3247,13 @@
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/xxhashjs": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz",
|
||||
@@ -2721,9 +3275,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz",
|
||||
"integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==",
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
|
||||
"integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"playwright": "^1.52.0",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"postcss-import": "^16.1.1",
|
||||
"postcss-import-url": "^7.2.0",
|
||||
"postcss-import-url": "^1.0.0",
|
||||
"postcss-url": "^10.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
19
web/build.sh
19
web/build.sh
@@ -39,9 +39,18 @@ for the JavaScript code in this page.
|
||||
mkdir -p static/locales
|
||||
cp ../lib/localization/locales/*.json static/locales/
|
||||
|
||||
for file in js/*.mjs js/worker/*.mjs; do
|
||||
esbuild "${file}" --sourcemap --bundle --minify --outfile=static/"${file}" --banner:js="${LICENSE}"
|
||||
gzip -f -k -n static/${file}
|
||||
zstd -f -k --ultra -22 static/${file}
|
||||
brotli -fZk static/${file}
|
||||
shopt -s nullglob globstar
|
||||
|
||||
for file in js/**/*.ts js/**/*.mjs; do
|
||||
out="static/${file}"
|
||||
if [[ "$file" == *.ts ]]; then
|
||||
out="static/${file%.ts}.mjs"
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$out")"
|
||||
|
||||
esbuild "$file" --sourcemap --bundle --minify --outfile="$out" --banner:js="$LICENSE"
|
||||
gzip -f -k -n "$out"
|
||||
zstd -f -k --ultra -22 "$out"
|
||||
brotli -fZk "$out"
|
||||
done
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
type ProgressCallback = (nonce: number) => void;
|
||||
|
||||
interface ProcessOptions {
|
||||
basePrefix: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
const getHardwareConcurrency = () =>
|
||||
navigator.hardwareConcurrency !== undefined ? navigator.hardwareConcurrency : 1;
|
||||
|
||||
export default function process(
|
||||
{ basePrefix, version },
|
||||
data,
|
||||
difficulty = 5,
|
||||
signal = null,
|
||||
progressCallback = null,
|
||||
threads = Math.trunc(Math.max(navigator.hardwareConcurrency / 2, 1)),
|
||||
) {
|
||||
options: ProcessOptions,
|
||||
data: string,
|
||||
difficulty: number = 5,
|
||||
signal: AbortSignal | null = null,
|
||||
progressCallback?: ProgressCallback,
|
||||
threads: number = Math.trunc(Math.max(getHardwareConcurrency() / 2, 1)),
|
||||
): Promise<string> {
|
||||
console.debug("fast algo");
|
||||
|
||||
let workerMethod = window.crypto !== undefined ? "webcrypto" : "purejs";
|
||||
@@ -16,13 +26,17 @@ export default function process(
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let webWorkerURL = `${basePrefix}/.within.website/x/cmd/anubis/static/js/worker/sha256-${workerMethod}.mjs?cacheBuster=${version}`;
|
||||
let webWorkerURL = `${options.basePrefix}/.within.website/x/cmd/anubis/static/js/worker/sha256-${workerMethod}.mjs?cacheBuster=${options.version}`;
|
||||
|
||||
console.log(webWorkerURL);
|
||||
|
||||
const workers = [];
|
||||
const workers: Worker[] = [];
|
||||
let settled = false;
|
||||
|
||||
const onAbort = () => {
|
||||
console.log("PoW aborted");
|
||||
cleanup();
|
||||
reject(new DOMException("Aborted", "AbortError"));
|
||||
};
|
||||
|
||||
const cleanup = () => {
|
||||
if (settled) {
|
||||
return;
|
||||
@@ -34,12 +48,6 @@ export default function process(
|
||||
}
|
||||
};
|
||||
|
||||
const onAbort = () => {
|
||||
console.log("PoW aborted");
|
||||
cleanup();
|
||||
reject(new DOMException("Aborted", "AbortError"));
|
||||
};
|
||||
|
||||
if (signal != null) {
|
||||
if (signal.aborted) {
|
||||
return onAbort();
|
||||
@@ -1,4 +1,4 @@
|
||||
import fast from "./fast.mjs";
|
||||
import fast from "./fast";
|
||||
|
||||
export default {
|
||||
fast: fast,
|
||||
@@ -1,20 +1,24 @@
|
||||
import algorithms from "./algorithms/index.mjs";
|
||||
import algorithms from "./algorithms";
|
||||
|
||||
const defaultDifficulty = 4;
|
||||
|
||||
const status = document.getElementById("status");
|
||||
const difficultyInput = document.getElementById("difficulty-input");
|
||||
const algorithmSelect = document.getElementById("algorithm-select");
|
||||
const compareSelect = document.getElementById("compare-select");
|
||||
const header = document.getElementById("table-header");
|
||||
const headerCompare = document.getElementById("table-header-compare");
|
||||
const results = document.getElementById("results");
|
||||
const status: HTMLParagraphElement = document.getElementById("status") as HTMLParagraphElement;
|
||||
const difficultyInput: HTMLInputElement = document.getElementById("difficulty-input") as HTMLInputElement;
|
||||
const algorithmSelect: HTMLSelectElement = document.getElementById("algorithm-select") as HTMLSelectElement;
|
||||
const compareSelect: HTMLSelectElement = document.getElementById("compare-select") as HTMLSelectElement;
|
||||
const header: HTMLTableRowElement = document.getElementById("table-header") as HTMLTableRowElement;
|
||||
const headerCompare: HTMLTableSectionElement = document.getElementById("table-header-compare") as HTMLTableSectionElement;
|
||||
const results: HTMLTableRowElement = document.getElementById("results") as HTMLTableRowElement;
|
||||
|
||||
const setupControls = () => {
|
||||
difficultyInput.value = defaultDifficulty;
|
||||
if (defaultDifficulty == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
difficultyInput.value = defaultDifficulty.toString();
|
||||
for (const alg of Object.keys(algorithms)) {
|
||||
const option1 = document.createElement("option");
|
||||
algorithmSelect.append(option1);
|
||||
algorithmSelect?.append(option1);
|
||||
const option2 = document.createElement("option");
|
||||
compareSelect.append(option2);
|
||||
option1.value = option1.innerText = option2.value = option2.innerText = alg;
|
||||
@@ -116,13 +120,13 @@ const benchmarkLoop = async (controller) => {
|
||||
await benchmarkLoop(controller);
|
||||
};
|
||||
|
||||
let controller = null;
|
||||
let controller: AbortController | null = null;
|
||||
const reset = () => {
|
||||
stats.time = stats.iters = 0;
|
||||
comparison.time = comparison.iters = 0;
|
||||
results.innerHTML = status.innerText = "";
|
||||
|
||||
const table = results.parentElement;
|
||||
const table = results.parentElement as HTMLElement;
|
||||
if (compareSelect.value !== "NONE") {
|
||||
table.style.gridTemplateColumns = "repeat(4,auto)";
|
||||
header.style.display = "none";
|
||||
@@ -1,12 +1,21 @@
|
||||
import algorithms from "./algorithms/index.mjs";
|
||||
import algorithms from "./algorithms";
|
||||
|
||||
// from Xeact
|
||||
const u = (url = "", params = {}) => {
|
||||
const u = (url: string = "", params: Record<string, any> = {}) => {
|
||||
let result = new URL(url, window.location.href);
|
||||
Object.entries(params).forEach(([k, v]) => result.searchParams.set(k, v));
|
||||
return result.toString();
|
||||
};
|
||||
|
||||
const j = (id: string): any | null => {
|
||||
const elem = document.getElementById(id);
|
||||
if (elem === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return JSON.parse(elem.textContent);
|
||||
};
|
||||
|
||||
const imageURL = (mood, cacheBuster, basePrefix) =>
|
||||
u(`${basePrefix}/.within.website/x/cmd/anubis/static/img/${mood}.webp`, {
|
||||
cacheBuster,
|
||||
@@ -14,9 +23,10 @@ const imageURL = (mood, cacheBuster, basePrefix) =>
|
||||
|
||||
// Detect available languages by loading the manifest
|
||||
const getAvailableLanguages = async () => {
|
||||
const basePrefix = JSON.parse(
|
||||
document.getElementById("anubis_base_prefix").textContent,
|
||||
);
|
||||
const basePrefix = j("anubis_base_prefix");
|
||||
if (basePrefix === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${basePrefix}/.within.website/x/cmd/anubis/static/locales/manifest.json`);
|
||||
@@ -38,9 +48,11 @@ const getBrowserLanguage = async () =>
|
||||
|
||||
// Load translations from JSON files
|
||||
const loadTranslations = async (lang) => {
|
||||
const basePrefix = JSON.parse(
|
||||
document.getElementById("anubis_base_prefix").textContent,
|
||||
);
|
||||
const basePrefix = j("anubis_base_prefix");
|
||||
if (basePrefix === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${basePrefix}/.within.website/x/cmd/anubis/static/locales/${lang}.json`);
|
||||
return await response.json();
|
||||
@@ -54,9 +66,10 @@ const loadTranslations = async (lang) => {
|
||||
};
|
||||
|
||||
const getRedirectUrl = () => {
|
||||
const publicUrl = JSON.parse(
|
||||
document.getElementById("anubis_public_url").textContent,
|
||||
);
|
||||
const publicUrl = j("anubis_public_url");
|
||||
if (publicUrl === null) {
|
||||
return;
|
||||
}
|
||||
if (publicUrl && window.location.href.startsWith(publicUrl)) {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
return urlParams.get('redir');
|
||||
@@ -91,16 +104,14 @@ const t = (key) => translations[`js_${key}`] || translations[key] || key;
|
||||
value: navigator.cookieEnabled,
|
||||
},
|
||||
];
|
||||
const status = document.getElementById("status");
|
||||
const image = document.getElementById("image");
|
||||
const title = document.getElementById("title");
|
||||
const progress = document.getElementById("progress");
|
||||
const anubisVersion = JSON.parse(
|
||||
document.getElementById("anubis_version").textContent,
|
||||
);
|
||||
const basePrefix = JSON.parse(
|
||||
document.getElementById("anubis_base_prefix").textContent,
|
||||
);
|
||||
|
||||
const status: HTMLParagraphElement = document.getElementById("status") as HTMLParagraphElement;
|
||||
const image: HTMLImageElement = document.getElementById("image") as HTMLImageElement;
|
||||
const title: HTMLHeadingElement = document.getElementById("title") as HTMLHeadingElement;
|
||||
const progress: HTMLDivElement = document.getElementById("progress") as HTMLDivElement;
|
||||
|
||||
const anubisVersion = j("anubis_version");
|
||||
const basePrefix = j("anubis_base_prefix");
|
||||
const details = document.querySelector("details");
|
||||
let userReadDetails = false;
|
||||
|
||||
@@ -132,9 +143,7 @@ const t = (key) => translations[`js_${key}`] || translations[key] || key;
|
||||
}
|
||||
}
|
||||
|
||||
const { challenge, rules } = JSON.parse(
|
||||
document.getElementById("anubis_challenge").textContent,
|
||||
);
|
||||
const { challenge, rules } = j("anubis_challenge");
|
||||
|
||||
const process = algorithms[rules.algorithm];
|
||||
if (!process) {
|
||||
@@ -182,7 +191,9 @@ const t = (key) => translations[`js_${key}`] || translations[key] || key;
|
||||
const probability = Math.pow(1 - likelihood, iters);
|
||||
const distance = (1 - Math.pow(probability, 2)) * 100;
|
||||
progress["aria-valuenow"] = distance;
|
||||
progress.firstElementChild.style.width = `${distance}%`;
|
||||
if (progress.firstElementChild !== null) {
|
||||
(progress.firstElementChild as HTMLElement).style.width = `${distance}%`;
|
||||
}
|
||||
|
||||
if (probability < 0.1 && !showingApology) {
|
||||
status.append(
|
||||
@@ -197,7 +208,7 @@ const t = (key) => translations[`js_${key}`] || translations[key] || key;
|
||||
console.log({ hash, nonce });
|
||||
|
||||
if (userReadDetails) {
|
||||
const container = document.getElementById("progress");
|
||||
const container: HTMLDivElement = document.getElementById("progress") as HTMLDivElement;
|
||||
|
||||
// Style progress bar as a continue button
|
||||
container.style.display = "flex";
|
||||
@@ -6,7 +6,7 @@ const calculateSHA256 = (text) => {
|
||||
return hash.digest();
|
||||
};
|
||||
|
||||
function toHexString(arr) {
|
||||
function toHexString(arr: Uint8Array): string {
|
||||
return Array.from(arr)
|
||||
.map((c) => c.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
@@ -1,10 +1,11 @@
|
||||
const encoder = new TextEncoder();
|
||||
const calculateSHA256 = async (input) => {
|
||||
|
||||
const calculateSHA256 = async (input: string) => {
|
||||
const data = encoder.encode(input);
|
||||
return await crypto.subtle.digest("SHA-256", data);
|
||||
};
|
||||
|
||||
const toHexString = (byteArray) => {
|
||||
const toHexString = (byteArray: Uint8Array) => {
|
||||
return byteArray.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user