Compare commits

..

8 Commits

Author SHA1 Message Date
437d1a3329 v26.2.3
Some checks failed
Default (tags) / security (push) Failing after 3s
Default (tags) / test (push) Failing after 3s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-03-25 07:26:47 +00:00
746d93663d fix(repo): no changes to commit 2026-03-25 07:26:47 +00:00
a3f3fee253 v26.2.2
Some checks failed
Default (tags) / security (push) Failing after 4s
Default (tags) / test (push) Failing after 5s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-03-25 07:22:17 +00:00
53dee1fffc fix(proxy): improve connection cleanup and route validation handling 2026-03-25 07:22:17 +00:00
34dc0cb9b6 v26.2.1
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-03-23 11:11:55 +00:00
c83c43194b fix(rustproxy-http): include the upstream request URL when caching H3 Alt-Svc discoveries 2026-03-23 11:11:55 +00:00
d026d7c266 v26.2.0
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-03-23 09:42:07 +00:00
3b01144c51 feat(protocol-cache): add sliding TTL re-probing and eviction for backend protocol detection 2026-03-23 09:42:07 +00:00
16 changed files with 910 additions and 661 deletions

View File

@@ -1,5 +1,31 @@
# Changelog # Changelog
## 2026-03-25 - 26.2.3 - fix(repo)
no changes to commit
## 2026-03-25 - 26.2.2 - fix(proxy)
improve connection cleanup and route validation handling
- add timeouts for HTTP/1 upstream connection drivers to prevent lingering tasks
- ensure QUIC relay sessions cancel and abort background tasks on drop
- avoid registering unnamed routes as duplicates and label unnamed catch-all conflicts clearly
- fix offset mapping route helper to forward only remaining route options without overriding derived values
- update project config filename and toolchain versions for the current build setup
## 2026-03-23 - 26.2.1 - fix(rustproxy-http)
include the upstream request URL when caching H3 Alt-Svc discoveries
- Tracks the request path that triggered Alt-Svc discovery in connection activity state
- Adds request URL context to Alt-Svc debug logging and protocol cache insertion reasons for better traceability
## 2026-03-23 - 26.2.0 - feat(protocol-cache)
add sliding TTL re-probing and eviction for backend protocol detection
- extend protocol cache entries and metrics with last accessed and last probed timestamps
- trigger periodic ALPN re-probes for cached H1/H2 entries while keeping active entries alive with a sliding 1 day TTL
- log protocol transitions with reasons and evict cache entries when all protocol fallback attempts fail
## 2026-03-22 - 26.1.0 - feat(rustproxy-http) ## 2026-03-22 - 26.1.0 - feat(rustproxy-http)
add protocol failure suppression, h3 fallback escalation, and protocol cache metrics exposure add protocol failure suppression, h3 fallback escalation, and protocol cache metrics exposure

770
deno.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
Copyright (c) 2019 Lossless GmbH (hello@lossless.com) Copyright (c) 2019 Task Venture Capital GmbH (hello@task.vc)
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,6 +1,6 @@
{ {
"name": "@push.rocks/smartproxy", "name": "@push.rocks/smartproxy",
"version": "26.1.0", "version": "26.2.3",
"private": false, "private": false,
"description": "A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.", "description": "A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.",
"main": "dist_ts/index.js", "main": "dist_ts/index.js",
@@ -16,13 +16,13 @@
"buildDocs": "tsdoc" "buildDocs": "tsdoc"
}, },
"devDependencies": { "devDependencies": {
"@git.zone/tsbuild": "^4.3.0", "@git.zone/tsbuild": "^4.4.0",
"@git.zone/tsrun": "^2.0.1", "@git.zone/tsrun": "^2.0.2",
"@git.zone/tsrust": "^1.3.0", "@git.zone/tsrust": "^1.3.2",
"@git.zone/tstest": "^3.5.0", "@git.zone/tstest": "^3.6.0",
"@push.rocks/smartserve": "^2.0.1", "@push.rocks/smartserve": "^2.0.3",
"@types/node": "^25.5.0", "@types/node": "^25.5.0",
"typescript": "^5.9.3", "typescript": "^6.0.2",
"why-is-node-running": "^3.2.2" "why-is-node-running": "^3.2.2"
}, },
"dependencies": { "dependencies": {
@@ -41,7 +41,7 @@
"dist_ts_web/**/*", "dist_ts_web/**/*",
"assets/**/*", "assets/**/*",
"cli.js", "cli.js",
"npmextra.json", ".smartconfig.json",
"readme.md", "readme.md",
"changelog.md" "changelog.md"
], ],

469
pnpm-lock.yaml generated
View File

@@ -25,26 +25,26 @@ importers:
version: 10.2.4 version: 10.2.4
devDependencies: devDependencies:
'@git.zone/tsbuild': '@git.zone/tsbuild':
specifier: ^4.3.0 specifier: ^4.4.0
version: 4.3.0 version: 4.4.0
'@git.zone/tsrun': '@git.zone/tsrun':
specifier: ^2.0.1 specifier: ^2.0.2
version: 2.0.1 version: 2.0.2
'@git.zone/tsrust': '@git.zone/tsrust':
specifier: ^1.3.0 specifier: ^1.3.2
version: 1.3.0 version: 1.3.2
'@git.zone/tstest': '@git.zone/tstest':
specifier: ^3.5.0 specifier: ^3.6.0
version: 3.5.0(socks@2.8.7)(typescript@5.9.3) version: 3.6.0(socks@2.8.7)(typescript@6.0.2)
'@push.rocks/smartserve': '@push.rocks/smartserve':
specifier: ^2.0.1 specifier: ^2.0.3
version: 2.0.1 version: 2.0.3
'@types/node': '@types/node':
specifier: ^25.5.0 specifier: ^25.5.0
version: 25.5.0 version: 25.5.0
typescript: typescript:
specifier: ^5.9.3 specifier: ^6.0.2
version: 5.9.3 version: 6.0.2
why-is-node-running: why-is-node-running:
specifier: ^3.2.2 specifier: ^3.2.2
version: 3.2.2 version: 3.2.2
@@ -414,28 +414,28 @@ packages:
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
'@git.zone/tsbuild@4.3.0': '@git.zone/tsbuild@4.4.0':
resolution: {integrity: sha512-lb6eMQ8RQPaJqAB4kC++GIElOiTAH1pClmoND/q7XHuiMZxv6cXz2/U/sZt339mon2c40dXRG2tkLF2jRsP0pQ==} resolution: {integrity: sha512-98igHfppi6blFYDyzNukNkj4FUO5ZlyXEaSyJh8vCkkZM8SyAgfZj+NUWA1D1iaPXE58UvK1Pt/o8p8iI9UHHw==}
hasBin: true hasBin: true
'@git.zone/tsbundle@2.9.1': '@git.zone/tsbundle@2.10.0':
resolution: {integrity: sha512-JW1xjSv7UjAm2lwAQPxhCWs14wqs+UIq5FqIGUPuI6rrDBWIMT2d0gpP6iP6TqXqgm6XpBlfU4rHcHheUXzXbQ==} resolution: {integrity: sha512-dw2VFlgKssDlCxg92wSPiiAKwfCjJBOEOYXq1xO91OpjQLOkyogCxSLy0jzQ2BYnt4qmBnapjamzYzVjCr4CWg==}
hasBin: true hasBin: true
'@git.zone/tspublish@1.11.2': '@git.zone/tspublish@1.11.5':
resolution: {integrity: sha512-BcGap1OzXDgXpfQXMh9W17r/CkWNhPsJ3WzjG2wrGE+ePUJCJAm9w6+J8G5WdZZcZKPqTB07cp707LbSiksc5A==} resolution: {integrity: sha512-3tCGhVbH6S/17n3A6Tc6H+ncRdxxbTT0ABcj8S1wRLA8YuBSj9bY7k6uj/iFRy/B/OepB94m1goCiaWESdcZYg==}
hasBin: true hasBin: true
'@git.zone/tsrun@2.0.1': '@git.zone/tsrun@2.0.2':
resolution: {integrity: sha512-NEcnsjvlC1o3Z6SS3VhKCf6Ev+Sh4EAinmggslrIR/ppMrvjDbXNFXoyr3PB+GLeSAR0JRZ1fGvVYjpEzjBdIg==} resolution: {integrity: sha512-Rnp/wYHzI8A1pVBKOOePRJgQiBZdW+GEjpQk2uhvXz6A+ljUV2SXKc7NpQVVDsjEZaNFeAI9jMYOdk3lm3yMDA==}
hasBin: true hasBin: true
'@git.zone/tsrust@1.3.0': '@git.zone/tsrust@1.3.2':
resolution: {integrity: sha512-dvmTAiM04Pkd7J1Gail3fu7aasmILQhC5vKL71/g6HYhpvl16/c+Dj3We5G4HsFr0jvAr+Xu570ZGEuZrtRcCg==} resolution: {integrity: sha512-bUGomPk++He47Q6rnd9bihX6qoYtXgp9BtroBnNADk3q8WGyHivAcPwqIe4Bk32eByzW1Acc37u/h5gb/V8ekA==}
hasBin: true hasBin: true
'@git.zone/tstest@3.5.0': '@git.zone/tstest@3.6.0':
resolution: {integrity: sha512-ugIJzdVkbgqSSw08SZajE7TB01GIYjEAmIy67O5skhvOyszGifwzJdR+8dS1VbQGlUUWQZMGQ2IowllHbAZYJQ==} resolution: {integrity: sha512-5D6COywCXmCqeUB8v6/kOzjEWCTKTUTI3ZB99ebwEibENFXnFBoVxNSRKN0pSmBYlgBEkT7DLNfTfp5tclSg8A==}
hasBin: true hasBin: true
'@img/colour@1.1.0': '@img/colour@1.1.0':
@@ -783,8 +783,8 @@ packages:
'@napi-rs/wasm-runtime@1.1.1': '@napi-rs/wasm-runtime@1.1.1':
resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==}
'@oxc-project/types@0.99.0': '@oxc-project/types@0.122.0':
resolution: {integrity: sha512-LLDEhXB7g1m5J+woRSgfKsFPS3LhR9xRhTeIoEBm5WrkwMxn6eZ0Ld0c0K5eHB57ChZX6I3uSmmLjZ8pcjlRcw==} resolution: {integrity: sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==}
'@pdf-lib/standard-fonts@1.0.0': '@pdf-lib/standard-fonts@1.0.0':
resolution: {integrity: sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==} resolution: {integrity: sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==}
@@ -858,12 +858,12 @@ packages:
'@push.rocks/lik@6.3.1': '@push.rocks/lik@6.3.1':
resolution: {integrity: sha512-UWDwGBaVx5yPtAFXqDDBtQZCzETUOA/7myQIXb+YBsuiIw4yQuhNZ23uY2ChQH2Zn6DLqdNSgQcYC0WywMZBNQ==} resolution: {integrity: sha512-UWDwGBaVx5yPtAFXqDDBtQZCzETUOA/7myQIXb+YBsuiIw4yQuhNZ23uY2ChQH2Zn6DLqdNSgQcYC0WywMZBNQ==}
'@push.rocks/lik@6.4.0':
resolution: {integrity: sha512-GCdXyF2a6NP+i0W6Mib1PjtA6JGrl6Ae17SbaQwqTscn4JHNta6xm9r+D8/b83XGZsoU903FlJZli3YqJCxT9Q==}
'@push.rocks/mongodump@1.1.0': '@push.rocks/mongodump@1.1.0':
resolution: {integrity: sha512-kW0ZUGyf1e4nwloVwBQjNId+MzgTcNS834C+RxH21i1NqyOubbpWZtJtPP+K+s35nSJRyCTy3ICfBMdDBTAm2w==} resolution: {integrity: sha512-kW0ZUGyf1e4nwloVwBQjNId+MzgTcNS834C+RxH21i1NqyOubbpWZtJtPP+K+s35nSJRyCTy3ICfBMdDBTAm2w==}
'@push.rocks/npmextra@5.3.3':
resolution: {integrity: sha512-snLpSHwaQ5OXlZzF1KX/FY71W5LwajjBzor82Vue0smjEPnSeUPY5/JcVdMwtdprdJe13pc/EQQuIiL/zw4/yg==}
'@push.rocks/qenv@6.1.3': '@push.rocks/qenv@6.1.3':
resolution: {integrity: sha512-+z2hsAU/7CIgpYLFqvda8cn9rUBMHqLdQLjsFfRn5jPoD7dJ5rFlpkbhfM4Ws8mHMniwWaxGKo+q/YBhtzRBLg==} resolution: {integrity: sha512-+z2hsAU/7CIgpYLFqvda8cn9rUBMHqLdQLjsFfRn5jPoD7dJ5rFlpkbhfM4Ws8mHMniwWaxGKo+q/YBhtzRBLg==}
@@ -888,6 +888,9 @@ packages:
'@push.rocks/smartclickhouse@2.2.0': '@push.rocks/smartclickhouse@2.2.0':
resolution: {integrity: sha512-eTzKiREIPSzL1kPkVyD6vEbn+WV/DvQqDjP67VlhNlQGbRcemnJG/eLrUUR1ytmdIqnsZGEK6UYBgyj5nhzLNQ==} resolution: {integrity: sha512-eTzKiREIPSzL1kPkVyD6vEbn+WV/DvQqDjP67VlhNlQGbRcemnJG/eLrUUR1ytmdIqnsZGEK6UYBgyj5nhzLNQ==}
'@push.rocks/smartconfig@6.1.0':
resolution: {integrity: sha512-B+xh63PhGAsSwuRyCKXr4PAjJ4HoVKhNysi67OGY6gGqGm6uopgEW1cvrUZ7T5ZSck9KlVx7ZTugbqm6dqBK1Q==}
'@push.rocks/smartcrypto@2.0.4': '@push.rocks/smartcrypto@2.0.4':
resolution: {integrity: sha512-1+/5bsjyataf5uUkUNnnVXGRAt+gHVk1KDzozjTqgqJxHvQk1d9fVDohL6CxUhUucTPtu5VR5xNBiV8YCDuGyw==} resolution: {integrity: sha512-1+/5bsjyataf5uUkUNnnVXGRAt+gHVk1KDzozjTqgqJxHvQk1d9fVDohL6CxUhUucTPtu5VR5xNBiV8YCDuGyw==}
@@ -1017,8 +1020,8 @@ packages:
'@push.rocks/smartrx@3.0.10': '@push.rocks/smartrx@3.0.10':
resolution: {integrity: sha512-USjIYcsSfzn14cwOsxgq/bBmWDTTzy3ouWAnW5NdMyRRzEbmeNrvmy6TRqNeDlJ2PsYNTt1rr/zGUqvIy72ITg==} resolution: {integrity: sha512-USjIYcsSfzn14cwOsxgq/bBmWDTTzy3ouWAnW5NdMyRRzEbmeNrvmy6TRqNeDlJ2PsYNTt1rr/zGUqvIy72ITg==}
'@push.rocks/smartserve@2.0.1': '@push.rocks/smartserve@2.0.3':
resolution: {integrity: sha512-YQb2qexfCzCqOlLWBBXKMg6xG4zahCPAxomz/KEKAwHtW6wMTtuHKSTSkRTQ0vl9jssLMAmRz2OyafiL9XGJXQ==} resolution: {integrity: sha512-PttdFlh61lsDNSRvRCSlKjRzuxgD3WP2XLuBNXu1hLfqLpQXDESj0ZCRPDZslLZsyFT5aHP9godb4D4L3bzHWA==}
'@push.rocks/smartshell@3.3.8': '@push.rocks/smartshell@3.3.8':
resolution: {integrity: sha512-t9J/py0vnea4ZtOs7Anc9dc6lcvg6EDvYBw5eE1mB+KUWxMQf/ROIQwWMo6B9SMNY4JS2UwvfuJQJ8makP/7Tg==} resolution: {integrity: sha512-t9J/py0vnea4ZtOs7Anc9dc6lcvg6EDvYBw5eE1mB+KUWxMQf/ROIQwWMo6B9SMNY4JS2UwvfuJQJ8makP/7Tg==}
@@ -1029,8 +1032,8 @@ packages:
'@push.rocks/smartstate@2.0.27': '@push.rocks/smartstate@2.0.27':
resolution: {integrity: sha512-q4UKir7GV3hakJWXQR4DoA4tUVwT5GRkJ/MtanHYF0wZLHfS19+nGmyO9y974zk3eT9hmy3+Lq5cKtU2W6+Y3w==} resolution: {integrity: sha512-q4UKir7GV3hakJWXQR4DoA4tUVwT5GRkJ/MtanHYF0wZLHfS19+nGmyO9y974zk3eT9hmy3+Lq5cKtU2W6+Y3w==}
'@push.rocks/smartstorage@6.0.1': '@push.rocks/smartstorage@6.3.2':
resolution: {integrity: sha512-W5PEVwO0J2K9YUZRTbKXadC11h6/IBzzqU+P0TIE/xpJZC4K1duEXwEhxGWcbfhCkPRRa51xH8Z5mAmzzm8qxA==} resolution: {integrity: sha512-g8rXlVZ+6iKmzNoybtwQntdb7EWA6WnVmbXNOdwDKWR8w4o/7UMErj+H5mt57iqYIy1pzQAoTb8IWJNsti7XQw==}
'@push.rocks/smartstream@3.4.0': '@push.rocks/smartstream@3.4.0':
resolution: {integrity: sha512-kePb44W9n5K96zj2Ms3K4xnYbNXP5AfxDd86zZMDQ1/T10nvkIpL9m5w4lG/VJ4KAsWFs81S87BkkcjhhrY5Kw==} resolution: {integrity: sha512-kePb44W9n5K96zj2Ms3K4xnYbNXP5AfxDd86zZMDQ1/T10nvkIpL9m5w4lG/VJ4KAsWFs81S87BkkcjhhrY5Kw==}
@@ -1050,8 +1053,8 @@ packages:
'@push.rocks/smartversion@3.0.5': '@push.rocks/smartversion@3.0.5':
resolution: {integrity: sha512-8MZSo1yqyaKxKq0Q5N188l4un++9GFWVbhCAX5mXJwewZHn97ujffTeL+eOQYpWFTEpUhaq1QhL4NhqObBCt1Q==} resolution: {integrity: sha512-8MZSo1yqyaKxKq0Q5N188l4un++9GFWVbhCAX5mXJwewZHn97ujffTeL+eOQYpWFTEpUhaq1QhL4NhqObBCt1Q==}
'@push.rocks/smartwatch@6.3.0': '@push.rocks/smartwatch@6.4.0':
resolution: {integrity: sha512-TeZ1PGBoBMpC4/CK8StIj5InEiFfKp7xWJSm3aYMjB/uaoeRP0vXqv1ORIC/TKYGJuEDuAXUsit8tZVjn0qT1Q==} resolution: {integrity: sha512-KDswRgE/siBmZRCsRA07MtW5oF4c9uQEBkwTGPIWneHzksbCDsvs/7agKFEL7WnNifLNwo8w1K1qoiVWkX1fvw==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@push.rocks/smartyaml@2.0.5': '@push.rocks/smartyaml@2.0.5':
@@ -1083,146 +1086,152 @@ packages:
resolution: {integrity: sha512-bqorOaGXPOuiOSV81luTKrTghg4O4NBRD0zyv7TIqmrMGf4a0uoozaUMp1X8vQdZW+y0gTzUJP9wkzAE6Cci0g==} resolution: {integrity: sha512-bqorOaGXPOuiOSV81luTKrTghg4O4NBRD0zyv7TIqmrMGf4a0uoozaUMp1X8vQdZW+y0gTzUJP9wkzAE6Cci0g==}
deprecated: This package has been deprecated in favour of the new package at @push.rocks/smartpromise deprecated: This package has been deprecated in favour of the new package at @push.rocks/smartpromise
'@rolldown/binding-android-arm64@1.0.0-beta.52': '@rolldown/binding-android-arm64@1.0.0-rc.11':
resolution: {integrity: sha512-MBGIgysimZPqTDcLXI+i9VveijkP5C3EAncEogXhqfax6YXj1Tr2LY3DVuEOMIjWfMPMhtQSPup4fSTAmgjqIw==} resolution: {integrity: sha512-SJ+/g+xNnOh6NqYxD0V3uVN4W3VfnrGsC9/hoglicgTNfABFG9JjISvkkU0dNY84MNHLWyOgxP9v9Y9pX4S7+A==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [android] os: [android]
'@rolldown/binding-darwin-arm64@1.0.0-beta.52': '@rolldown/binding-darwin-arm64@1.0.0-rc.11':
resolution: {integrity: sha512-MmKeoLnKu1d9j6r19K8B+prJnIZ7u+zQ+zGQ3YHXGnr41rzE3eqQLovlkvoZnRoxDGPA4ps0pGiwXy6YE3lJyg==} resolution: {integrity: sha512-7WQgR8SfOPwmDZGFkThUvsmd/nwAWv91oCO4I5LS7RKrssPZmOt7jONN0cW17ydGC1n/+puol1IpoieKqQidmg==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@rolldown/binding-darwin-x64@1.0.0-beta.52': '@rolldown/binding-darwin-x64@1.0.0-rc.11':
resolution: {integrity: sha512-qpHedvQBmIjT8zdnjN3nWPR2qjQyJttbXniCEKKdHeAbZG9HyNPBUzQF7AZZGwmS9coQKL+hWg9FhWzh2dZ2IA==} resolution: {integrity: sha512-39Ks6UvIHq4rEogIfQBoBRusj0Q0nPVWIvqmwBLaT6aqQGIakHdESBVOPRRLacy4WwUPIx4ZKzfZ9PMW+IeyUQ==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@rolldown/binding-freebsd-x64@1.0.0-beta.52': '@rolldown/binding-freebsd-x64@1.0.0-rc.11':
resolution: {integrity: sha512-dDp7WbPapj/NVW0LSiH/CLwMhmLwwKb3R7mh2kWX+QW85X1DGVnIEyKh9PmNJjB/+suG1dJygdtdNPVXK1hylg==} resolution: {integrity: sha512-jfsm0ZHfhiqrvWjJAmzsqiIFPz5e7mAoCOPBNTcNgkiid/LaFKiq92+0ojH+nmJmKYkre4t71BWXUZDNp7vsag==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [freebsd] os: [freebsd]
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.52': '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.11':
resolution: {integrity: sha512-9e4l6vy5qNSliDPqNfR6CkBOAx6PH7iDV4OJiEJzajajGrVy8gc/IKKJUsoE52G8ud8MX6r3PMl97NfwgOzB7g==} resolution: {integrity: sha512-zjQaUtSyq1nVe3nxmlSCuR96T1LPlpvmJ0SZy0WJFEsV4kFbXcq2u68L4E6O0XeFj4aex9bEauqjW8UQBeAvfQ==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.52': '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.11':
resolution: {integrity: sha512-V48oDR84feRU2KRuzpALp594Uqlx27+zFsT6+BgTcXOtu7dWy350J1G28ydoCwKB+oxwsRPx2e7aeQnmd3YJbQ==} resolution: {integrity: sha512-WMW1yE6IOnehTcFE9eipFkm3XN63zypWlrJQ2iF7NrQ9b2LDRjumFoOGJE8RJJTJCTBAdmLMnJ8uVitACUUo1Q==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.52': '@rolldown/binding-linux-arm64-musl@1.0.0-rc.11':
resolution: {integrity: sha512-ENLmSQCWqSA/+YN45V2FqTIemg7QspaiTjlm327eUAMeOLdqmSOVVyrQexJGNTQ5M8sDYCgVAig2Kk01Ggmqaw==} resolution: {integrity: sha512-jfndI9tsfm4APzjNt6QdBkYwre5lRPUgHeDHoI7ydKUuJvz3lZeCfMsI56BZj+7BYqiKsJm7cfd/6KYV7ubrBg==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.52': '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.11':
resolution: {integrity: sha512-klahlb2EIFltSUubn/VLjuc3qxp1E7th8ukayPfdkcKvvYcQ5rJztgx8JsJSuAKVzKtNTqUGOhy4On71BuyV8g==} resolution: {integrity: sha512-ZlFgw46NOAGMgcdvdYwAGu2Q+SLFA9LzbJLW+iyMOJyhj5wk6P3KEE9Gct4xWwSzFoPI7JCdYmYMzVtlgQ+zfw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
'@rolldown/binding-linux-s390x-gnu@1.0.0-rc.11':
resolution: {integrity: sha512-hIOYmuT6ofM4K04XAZd3OzMySEO4K0/nc9+jmNcxNAxRi6c5UWpqfw3KMFV4MVFWL+jQsSh+bGw2VqmaPMTLyw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
'@rolldown/binding-linux-x64-gnu@1.0.0-rc.11':
resolution: {integrity: sha512-qXBQQO9OvkjjQPLdUVr7Nr2t3QTZI7s4KZtfw7HzBgjbmAPSFwSv4rmET9lLSgq3rH/ndA3ngv3Qb8l2njoPNA==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@rolldown/binding-linux-x64-musl@1.0.0-beta.52': '@rolldown/binding-linux-x64-musl@1.0.0-rc.11':
resolution: {integrity: sha512-UuA+JqQIgqtkgGN2c/AQ5wi8M6mJHrahz/wciENPTeI6zEIbbLGoth5XN+sQe2pJDejEVofN9aOAp0kaazwnVg==} resolution: {integrity: sha512-/tpFfoSTzUkH9LPY+cYbqZBDyyX62w5fICq9qzsHLL8uTI6BHip3Q9Uzft0wylk/i8OOwKik8OxW+QAhDmzwmg==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@rolldown/binding-openharmony-arm64@1.0.0-beta.52': '@rolldown/binding-openharmony-arm64@1.0.0-rc.11':
resolution: {integrity: sha512-1BNQW8u4ro8bsN1+tgKENJiqmvc+WfuaUhXzMImOVSMw28pkBKdfZtX2qJPADV3terx+vNJtlsgSGeb3+W6Jiw==} resolution: {integrity: sha512-mcp3Rio2w72IvdZG0oQ4bM2c2oumtwHfUfKncUM6zGgz0KgPz4YmDPQfnXEiY5t3+KD/i8HG2rOB/LxdmieK2g==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [openharmony] os: [openharmony]
'@rolldown/binding-wasm32-wasi@1.0.0-beta.52': '@rolldown/binding-wasm32-wasi@1.0.0-rc.11':
resolution: {integrity: sha512-K/p7clhCqJOQpXGykrFaBX2Dp9AUVIDHGc+PtFGBwg7V+mvBTv/tsm3LC3aUmH02H2y3gz4y+nUTQ0MLpofEEg==} resolution: {integrity: sha512-LXk5Hii1Ph9asuGRjBuz8TUxdc1lWzB7nyfdoRgI0WGPZKmCxvlKk8KfYysqtr4MfGElu/f/pEQRh8fcEgkrWw==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
cpu: [wasm32] cpu: [wasm32]
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.52': '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.11':
resolution: {integrity: sha512-a4EkXBtnYYsKipjS7QOhEBM4bU5IlR9N1hU+JcVEVeuTiaslIyhWVKsvf7K2YkQHyVAJ+7/A9BtrGqORFcTgng==} resolution: {integrity: sha512-dDwf5otnx0XgRY1yqxOC4ITizcdzS/8cQ3goOWv3jFAo4F+xQYni+hnMuO6+LssHHdJW7+OCVL3CoU4ycnh35Q==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@rolldown/binding-win32-ia32-msvc@1.0.0-beta.52': '@rolldown/binding-win32-x64-msvc@1.0.0-rc.11':
resolution: {integrity: sha512-5ZXcYyd4GxPA6QfbGrNcQjmjbuLGvfz6728pZMsQvGHI+06LT06M6TPtXvFvLgXtexc+OqvFe1yAIXJU1gob/w==} resolution: {integrity: sha512-LN4/skhSggybX71ews7dAj6r2geaMJfm3kMbK2KhFMg9B10AZXnKoLCVVgzhMHL0S+aKtr4p8QbAW8k+w95bAA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ia32]
os: [win32]
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.52':
resolution: {integrity: sha512-tzpnRQXJrSzb8Z9sm97UD3cY0toKOImx+xRKsDLX4zHaAlRXWh7jbaKBePJXEN7gNw7Nm03PBNwphdtA8KSUYQ==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
'@rolldown/pluginutils@1.0.0-beta.52': '@rolldown/pluginutils@1.0.0-rc.11':
resolution: {integrity: sha512-/L0htLJZbaZFL1g9OHOblTxbCYIGefErJjtYOwgl9ZqNx27P3L0SDfjhhHIss32gu5NWgnxuT2a2Hnnv6QGHKA==} resolution: {integrity: sha512-xQO9vbwBecJRv9EUcQ/y0dzSTJgA7Q6UVN7xp6B81+tBGSLVAK03yJ9NkJaUA7JFD91kbjxRSC/mDnmvXzbHoQ==}
'@rspack/binding-darwin-arm64@1.7.9': '@rspack/binding-darwin-arm64@1.7.10':
resolution: {integrity: sha512-64dgstte0If5czi9bA/cpOe0ryY6wC9AIQRtyJ3DlOF6Tt+y9cKkmUoGu3V+WYaYIZRT7HNk8V7kL8amVjFTYw==} resolution: {integrity: sha512-bsXi7I6TpH+a4L6okIUh1JDvwT+XcK/L7Yvhu5G2t5YYyd2fl5vMM5O9cePRpEb0RdqJZ3Z8i9WIWHap9aQ8Gw==}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@rspack/binding-darwin-x64@1.7.9': '@rspack/binding-darwin-x64@1.7.10':
resolution: {integrity: sha512-2QSLs3w4rLy4UUGVnIlkt6IlIKOzR1e0RPsq2FYQW6s3p9JrwRCtOeHohyh7EJSqF54dtfhe9UZSAwba3LqH1Q==} resolution: {integrity: sha512-h/kOGL1bUflDDYnbiUjaRE9kagJpour4FatGihueV03+cRGQ6jpde+BjUakqzMx65CeDbeYI6jAiPhElnlAtRw==}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@rspack/binding-linux-arm64-gnu@1.7.9': '@rspack/binding-linux-arm64-gnu@1.7.10':
resolution: {integrity: sha512-qhUGI/uVfvLmKWts4QkVHGL8yfUyJkblZs+OFD5Upa2y676EOsbQgWsCwX4xGB6Tv+TOzFP0SLh/UfO8ZfdE+w==} resolution: {integrity: sha512-Z4reus7UxGM4+JuhiIht8KuGP1KgM7nNhOlXUHcQCMswP/Rymj5oJQN3TDWgijFUZs09ULl8t3T+AQAVTd/WvA==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@rspack/binding-linux-arm64-musl@1.7.9': '@rspack/binding-linux-arm64-musl@1.7.10':
resolution: {integrity: sha512-VjfmR1hgO9n3L6MaE5KG+DXSrrLVqHHOkVcOtS2LMq3bjMTwbBywY7ycymcLnX5KJsol8d3ZGYep6IfSOt3lFA==} resolution: {integrity: sha512-LYaoVmWizG4oQ3g+St3eM5qxsyfH07kLirP7NJcDMgvu3eQ29MeyTZ3ugkgW6LvlmJue7eTQyf6CZlanoF5SSg==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@rspack/binding-linux-x64-gnu@1.7.9': '@rspack/binding-linux-x64-gnu@1.7.10':
resolution: {integrity: sha512-0kldV+3WTs/VYDWzxJ7K40hCW26IHtnk8xPK3whKoo1649rgeXXa0EdsU5P7hG8Ef5SWQjHHHZ/fuHYSO3Y6HA==} resolution: {integrity: sha512-aIm2G4Kcm3qxDTNqKarK0oaLY2iXnCmpRQQhAcMlR0aS2LmxL89XzVeRr9GFA1MzGrAsZONWCLkxQvn3WUbm4Q==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@rspack/binding-linux-x64-musl@1.7.9': '@rspack/binding-linux-x64-musl@1.7.10':
resolution: {integrity: sha512-Gi4872cFtc2d83FKATR6Qcf2VBa/tFCqffI/IwRRl6Hx5FulEBqx+tH7gAuRVF693vrbXNxK+FQ+k4iEsEJxrw==} resolution: {integrity: sha512-SIHQbAgB9IPH0H3H+i5rN5jo9yA/yTMq8b7XfRkTMvZ7P7MXxJ0dE8EJu3BmCLM19sqnTc2eX+SVfE8ZMDzghA==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@rspack/binding-wasm32-wasi@1.7.9': '@rspack/binding-wasm32-wasi@1.7.10':
resolution: {integrity: sha512-5QEzqo6EaolpuZmK6w/mgSueorgGnnzp7dJaAvBj6ECFIg/aLXhXXmWCWbxt7Ws2gKvG5/PgaxDqbUxYL51juA==} resolution: {integrity: sha512-J9HDXHD1tj+9FmX4+K3CTkO7dCE2bootlR37YuC2Owc0Lwl1/i2oGT71KHnMqI9faF/hipAaQM5OywkiiuNB7w==}
cpu: [wasm32] cpu: [wasm32]
'@rspack/binding-win32-arm64-msvc@1.7.9': '@rspack/binding-win32-arm64-msvc@1.7.10':
resolution: {integrity: sha512-MMqvcrIc8aOqTuHjWkjdzilvoZ3Hv07Od0Foogiyq3JMudsS3Wcmh7T1dFerGg19MOJcRUeEkrg2NQOMOQ6xDA==} resolution: {integrity: sha512-FaQGSCXH89nMOYW0bVp0bKQDQbrOEFFm7yedla7g6mkWlFVQo5UyBxid5wJUCqGJBtJepRxeRfByWiaI5nVGvg==}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@rspack/binding-win32-ia32-msvc@1.7.9': '@rspack/binding-win32-ia32-msvc@1.7.10':
resolution: {integrity: sha512-4kYYS+NZ2CuNbKjq40yB/UEyB51o1PHj5wpr+Y943oOJXpEKWU2Q4vkF8VEohPEcnA9cKVotYCnqStme+02suA==} resolution: {integrity: sha512-/66TNLOeM4R5dHhRWRVbMTgWghgxz+32ym0c/zGGXQRoMbz7210EoL40ALUgdBdeeREO8LoV+Mn7v8/QZCwHzw==}
cpu: [ia32] cpu: [ia32]
os: [win32] os: [win32]
'@rspack/binding-win32-x64-msvc@1.7.9': '@rspack/binding-win32-x64-msvc@1.7.10':
resolution: {integrity: sha512-1g+QyXXvs+838Un/4GaUvJfARDGHMCs15eXDYWBl5m/Skubyng8djWAgr6ag1+cVoJZXCPOvybTItcblWF3gbQ==} resolution: {integrity: sha512-SUa3v1W7PGFCy6AHRmDsm43/tkfaZFi1TN2oIk5aCdT9T51baDVBjAbehRDu9xFbK4piL3k7uqIVSIrKgVqk1g==}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
'@rspack/binding@1.7.9': '@rspack/binding@1.7.10':
resolution: {integrity: sha512-A56e0NdfNwbOSJoilMkxzaPuVYaKCNn1shuiwWnCIBmhV9ix1n9S1XvquDjkGyv+gCdR1+zfJBOa5DMB7htLHw==} resolution: {integrity: sha512-j+DPEaSJLRgasxXNpYQpvC7wUkQF5WoWPiTfm4fLczwlAmYwGSVkJiyWDrOlvVPiGGYiXIaXEjVWTw6fT6/vnA==}
'@rspack/core@1.7.9': '@rspack/core@1.7.10':
resolution: {integrity: sha512-VHuSKvRkuv42Ya+TxEGO0LE0r9+8P4tKGokmomj4R1f/Nu2vtS3yoaIMfC4fR6VuHGd3MZ+KTI0cNNwHfFcskw==} resolution: {integrity: sha512-dO7J0aHSa9Fg2kGT0+ZsM500lMdlNIyCHavIaz7dTDn6KXvFz1qbWQ/48x3OlNFw1mA0jxAjjw9e7h3sWQZUNg==}
engines: {node: '>=18.12.0'} engines: {node: '>=18.12.0'}
peerDependencies: peerDependencies:
'@swc/helpers': '>=0.5.1' '@swc/helpers': '>=0.5.1'
@@ -2933,8 +2942,8 @@ packages:
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
hasBin: true hasBin: true
rolldown@1.0.0-beta.52: rolldown@1.0.0-rc.11:
resolution: {integrity: sha512-Hbnpljue+JhMJrlOjQ1ixp9me7sUec7OjFvS+A1Qm8k8Xyxmw3ZhxFu7LlSXW1s9AX3POE9W9o2oqCEeR5uDmg==} resolution: {integrity: sha512-NRjoKMusSjfRbSYiH3VSumlkgFe7kYAa3pzVOsVYVFY3zb5d7nS+a3KGQ7hJKXuYWbzJKPVQ9Wxq2UvyK+ENpw==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true hasBin: true
@@ -2995,8 +3004,8 @@ packages:
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
smol-toml@1.6.0: smol-toml@1.6.1:
resolution: {integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==} resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==}
engines: {node: '>= 18'} engines: {node: '>= 18'}
socks-proxy-agent@8.0.5: socks-proxy-agent@8.0.5:
@@ -3162,8 +3171,8 @@ packages:
typed-query-selector@2.12.1: typed-query-selector@2.12.1:
resolution: {integrity: sha512-uzR+FzI8qrUEIu96oaeBJmd9E7CFEiQ3goA5qCVgc4s5llSubcfGHq9yUstZx/k4s9dXHVKsE35YWoFyvEqEHA==} resolution: {integrity: sha512-uzR+FzI8qrUEIu96oaeBJmd9E7CFEiQ3goA5qCVgc4s5llSubcfGHq9yUstZx/k4s9dXHVKsE35YWoFyvEqEHA==}
typescript@5.9.3: typescript@6.0.2:
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} resolution: {integrity: sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==}
engines: {node: '>=14.17'} engines: {node: '>=14.17'}
hasBin: true hasBin: true
@@ -3275,6 +3284,18 @@ packages:
utf-8-validate: utf-8-validate:
optional: true optional: true
ws@8.20.0:
resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
xml-parse-from-string@1.0.1: xml-parse-from-string@1.0.1:
resolution: {integrity: sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==} resolution: {integrity: sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==}
@@ -3938,9 +3959,9 @@ snapshots:
'@esbuild/win32-x64@0.27.4': '@esbuild/win32-x64@0.27.4':
optional: true optional: true
'@git.zone/tsbuild@4.3.0': '@git.zone/tsbuild@4.4.0':
dependencies: dependencies:
'@git.zone/tspublish': 1.11.2 '@git.zone/tspublish': 1.11.5
'@push.rocks/early': 4.0.4 '@push.rocks/early': 4.0.4
'@push.rocks/smartcli': 4.0.20 '@push.rocks/smartcli': 4.0.20
'@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartdelay': 3.0.5
@@ -3949,7 +3970,7 @@ snapshots:
'@push.rocks/smartlog': 3.2.1 '@push.rocks/smartlog': 3.2.1
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
typescript: 5.9.3 typescript: 6.0.2
transitivePeerDependencies: transitivePeerDependencies:
- '@nuxt/kit' - '@nuxt/kit'
- aws-crt - aws-crt
@@ -3960,11 +3981,11 @@ snapshots:
- supports-color - supports-color
- vue - vue
'@git.zone/tsbundle@2.9.1': '@git.zone/tsbundle@2.10.0':
dependencies: dependencies:
'@push.rocks/early': 4.0.4 '@push.rocks/early': 4.0.4
'@push.rocks/npmextra': 5.3.3
'@push.rocks/smartcli': 4.0.20 '@push.rocks/smartcli': 4.0.20
'@push.rocks/smartconfig': 6.1.0
'@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartfs': 1.5.0 '@push.rocks/smartfs': 1.5.0
'@push.rocks/smartinteract': 2.0.16 '@push.rocks/smartinteract': 2.0.16
@@ -3973,12 +3994,12 @@ snapshots:
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartspawn': 3.0.3 '@push.rocks/smartspawn': 3.0.3
'@rspack/core': 1.7.9 '@rspack/core': 1.7.10
'@types/html-minifier': 4.0.6 '@types/html-minifier': 4.0.6
esbuild: 0.27.4 esbuild: 0.27.4
html-minifier: 4.0.0 html-minifier: 4.0.0
rolldown: 1.0.0-beta.52 rolldown: 1.0.0-rc.11
typescript: 5.9.3 typescript: 6.0.2
transitivePeerDependencies: transitivePeerDependencies:
- '@nuxt/kit' - '@nuxt/kit'
- '@swc/helpers' - '@swc/helpers'
@@ -3986,11 +4007,11 @@ snapshots:
- supports-color - supports-color
- vue - vue
'@git.zone/tspublish@1.11.2': '@git.zone/tspublish@1.11.5':
dependencies: dependencies:
'@push.rocks/consolecolor': 2.0.3 '@push.rocks/consolecolor': 2.0.3
'@push.rocks/npmextra': 5.3.3
'@push.rocks/smartcli': 4.0.20 '@push.rocks/smartcli': 4.0.20
'@push.rocks/smartconfig': 6.1.0
'@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartfile': 13.1.2 '@push.rocks/smartfile': 13.1.2
'@push.rocks/smartfs': 1.5.0 '@push.rocks/smartfs': 1.5.0
@@ -4009,34 +4030,34 @@ snapshots:
- supports-color - supports-color
- vue - vue
'@git.zone/tsrun@2.0.1': '@git.zone/tsrun@2.0.2':
dependencies: dependencies:
'@push.rocks/smartfile': 13.1.2 '@push.rocks/smartfile': 13.1.2
'@push.rocks/smartshell': 3.3.8 '@push.rocks/smartshell': 3.3.8
tsx: 4.21.0 tsx: 4.21.0
'@git.zone/tsrust@1.3.0': '@git.zone/tsrust@1.3.2':
dependencies: dependencies:
'@push.rocks/early': 4.0.4 '@push.rocks/early': 4.0.4
'@push.rocks/npmextra': 5.3.3
'@push.rocks/smartcli': 4.0.20 '@push.rocks/smartcli': 4.0.20
'@push.rocks/smartconfig': 6.1.0
'@push.rocks/smartfile': 13.1.2 '@push.rocks/smartfile': 13.1.2
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
'@push.rocks/smartshell': 3.3.8 '@push.rocks/smartshell': 3.3.8
smol-toml: 1.6.0 smol-toml: 1.6.1
transitivePeerDependencies: transitivePeerDependencies:
- '@nuxt/kit' - '@nuxt/kit'
- react - react
- supports-color - supports-color
- vue - vue
'@git.zone/tstest@3.5.0(socks@2.8.7)(typescript@5.9.3)': '@git.zone/tstest@3.6.0(socks@2.8.7)(typescript@6.0.2)':
dependencies: dependencies:
'@git.zone/tsbundle': 2.9.1 '@git.zone/tsbundle': 2.10.0
'@git.zone/tsrun': 2.0.1 '@git.zone/tsrun': 2.0.2
'@push.rocks/consolecolor': 2.0.3 '@push.rocks/consolecolor': 2.0.3
'@push.rocks/qenv': 6.1.3 '@push.rocks/qenv': 6.1.3
'@push.rocks/smartbrowser': 2.0.11(typescript@5.9.3) '@push.rocks/smartbrowser': 2.0.11(typescript@6.0.2)
'@push.rocks/smartcrypto': 2.0.4 '@push.rocks/smartcrypto': 2.0.4
'@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartenv': 6.0.0 '@push.rocks/smartenv': 6.0.0
@@ -4050,14 +4071,14 @@ snapshots:
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrequest': 5.0.1 '@push.rocks/smartrequest': 5.0.1
'@push.rocks/smartserve': 2.0.1 '@push.rocks/smartserve': 2.0.3
'@push.rocks/smartshell': 3.3.8 '@push.rocks/smartshell': 3.3.8
'@push.rocks/smartstorage': 6.0.1 '@push.rocks/smartstorage': 6.3.2
'@push.rocks/smarttime': 4.2.3 '@push.rocks/smarttime': 4.2.3
'@push.rocks/smartwatch': 6.3.0 '@push.rocks/smartwatch': 6.4.0
'@types/ws': 8.18.1 '@types/ws': 8.18.1
figures: 6.1.0 figures: 6.1.0
ws: 8.19.0 ws: 8.20.0
transitivePeerDependencies: transitivePeerDependencies:
- '@aws-sdk/credential-providers' - '@aws-sdk/credential-providers'
- '@mongodb-js/zstd' - '@mongodb-js/zstd'
@@ -4513,7 +4534,7 @@ snapshots:
'@tybys/wasm-util': 0.10.1 '@tybys/wasm-util': 0.10.1
optional: true optional: true
'@oxc-project/types@0.99.0': {} '@oxc-project/types@0.122.0': {}
'@pdf-lib/standard-fonts@1.0.0': '@pdf-lib/standard-fonts@1.0.0':
dependencies: dependencies:
@@ -4684,6 +4705,15 @@ snapshots:
'@types/symbol-tree': 3.2.5 '@types/symbol-tree': 3.2.5
symbol-tree: 3.2.4 symbol-tree: 3.2.4
'@push.rocks/lik@6.4.0':
dependencies:
'@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartmatch': 2.0.0
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrx': 3.0.10
'@push.rocks/smarttime': 4.2.3
symbol-tree: 3.2.4
'@push.rocks/mongodump@1.1.0(socks@2.8.7)': '@push.rocks/mongodump@1.1.0(socks@2.8.7)':
dependencies: dependencies:
'@push.rocks/lik': 6.3.1 '@push.rocks/lik': 6.3.1
@@ -4702,23 +4732,6 @@ snapshots:
- snappy - snappy
- socks - socks
'@push.rocks/npmextra@5.3.3':
dependencies:
'@push.rocks/qenv': 6.1.3
'@push.rocks/smartfile': 11.2.7
'@push.rocks/smartjson': 5.2.0
'@push.rocks/smartlog': 3.2.1
'@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrx': 3.0.10
'@push.rocks/taskbuffer': 3.5.0
'@tsclass/tsclass': 9.5.0
transitivePeerDependencies:
- '@nuxt/kit'
- react
- supports-color
- vue
'@push.rocks/qenv@6.1.3': '@push.rocks/qenv@6.1.3':
dependencies: dependencies:
'@api.global/typedrequest': 3.2.5 '@api.global/typedrequest': 3.2.5
@@ -4748,11 +4761,11 @@ snapshots:
- react-native-b4a - react-native-b4a
- supports-color - supports-color
'@push.rocks/smartbrowser@2.0.11(typescript@5.9.3)': '@push.rocks/smartbrowser@2.0.11(typescript@6.0.2)':
dependencies: dependencies:
'@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartpdf': 4.2.0(typescript@5.9.3) '@push.rocks/smartpdf': 4.2.0(typescript@6.0.2)
'@push.rocks/smartpuppeteer': 2.0.5(typescript@5.9.3) '@push.rocks/smartpuppeteer': 2.0.5(typescript@6.0.2)
'@push.rocks/smartunique': 3.0.9 '@push.rocks/smartunique': 3.0.9
transitivePeerDependencies: transitivePeerDependencies:
- '@nuxt/kit' - '@nuxt/kit'
@@ -4811,6 +4824,23 @@ snapshots:
'@push.rocks/smarturl': 3.1.0 '@push.rocks/smarturl': 3.1.0
'@push.rocks/webrequest': 4.0.5 '@push.rocks/webrequest': 4.0.5
'@push.rocks/smartconfig@6.1.0':
dependencies:
'@push.rocks/qenv': 6.1.3
'@push.rocks/smartfile': 11.2.7
'@push.rocks/smartjson': 5.2.0
'@push.rocks/smartlog': 3.2.1
'@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrx': 3.0.10
'@push.rocks/taskbuffer': 3.5.0
'@tsclass/tsclass': 9.5.0
transitivePeerDependencies:
- '@nuxt/kit'
- react
- supports-color
- vue
'@push.rocks/smartcrypto@2.0.4': '@push.rocks/smartcrypto@2.0.4':
dependencies: dependencies:
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
@@ -5131,7 +5161,7 @@ snapshots:
'@push.rocks/smartpath@6.0.0': {} '@push.rocks/smartpath@6.0.0': {}
'@push.rocks/smartpdf@4.2.0(typescript@5.9.3)': '@push.rocks/smartpdf@4.2.0(typescript@6.0.2)':
dependencies: dependencies:
'@push.rocks/smartbuffer': 3.0.5 '@push.rocks/smartbuffer': 3.0.5
'@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartdelay': 3.0.5
@@ -5140,8 +5170,8 @@ snapshots:
'@push.rocks/smartnetwork': 4.4.0 '@push.rocks/smartnetwork': 4.4.0
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartpuppeteer': 2.0.5(typescript@5.9.3) '@push.rocks/smartpuppeteer': 2.0.5(typescript@6.0.2)
'@push.rocks/smartserve': 2.0.1 '@push.rocks/smartserve': 2.0.3
'@push.rocks/smartunique': 3.0.9 '@push.rocks/smartunique': 3.0.9
'@tsclass/tsclass': 9.5.0 '@tsclass/tsclass': 9.5.0
pdf-lib: 1.17.1 pdf-lib: 1.17.1
@@ -5166,11 +5196,11 @@ snapshots:
'@push.rocks/smartpromise@4.2.3': {} '@push.rocks/smartpromise@4.2.3': {}
'@push.rocks/smartpuppeteer@2.0.5(typescript@5.9.3)': '@push.rocks/smartpuppeteer@2.0.5(typescript@6.0.2)':
dependencies: dependencies:
'@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartshell': 3.3.8 '@push.rocks/smartshell': 3.3.8
puppeteer: 24.40.0(typescript@5.9.3) puppeteer: 24.40.0(typescript@6.0.2)
tree-kill: 1.2.2 tree-kill: 1.2.2
transitivePeerDependencies: transitivePeerDependencies:
- bare-abort-controller - bare-abort-controller
@@ -5221,7 +5251,7 @@ snapshots:
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
rxjs: 7.8.2 rxjs: 7.8.2
'@push.rocks/smartserve@2.0.1': '@push.rocks/smartserve@2.0.3':
dependencies: dependencies:
'@api.global/typedrequest': 3.2.5 '@api.global/typedrequest': 3.2.5
'@cfworker/json-schema': 4.1.1 '@cfworker/json-schema': 4.1.1
@@ -5260,7 +5290,7 @@ snapshots:
'@push.rocks/smartrx': 3.0.10 '@push.rocks/smartrx': 3.0.10
'@push.rocks/webstore': 2.0.20 '@push.rocks/webstore': 2.0.20
'@push.rocks/smartstorage@6.0.1': '@push.rocks/smartstorage@6.3.2':
dependencies: dependencies:
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
'@push.rocks/smartrust': 1.3.2 '@push.rocks/smartrust': 1.3.2
@@ -5301,11 +5331,12 @@ snapshots:
'@types/semver': 7.7.1 '@types/semver': 7.7.1
semver: 7.7.4 semver: 7.7.4
'@push.rocks/smartwatch@6.3.0': '@push.rocks/smartwatch@6.4.0':
dependencies: dependencies:
'@push.rocks/lik': 6.3.1 '@push.rocks/lik': 6.4.0
'@push.rocks/smartenv': 6.0.0 '@push.rocks/smartenv': 6.0.0
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrust': 1.3.2
'@push.rocks/smartrx': 3.0.10 '@push.rocks/smartrx': 3.0.10
chokidar: 5.0.0 chokidar: 5.0.0
picomatch: 4.0.3 picomatch: 4.0.3
@@ -5374,101 +5405,104 @@ snapshots:
'@pushrocks/smartpromise@4.0.2': {} '@pushrocks/smartpromise@4.0.2': {}
'@rolldown/binding-android-arm64@1.0.0-beta.52': '@rolldown/binding-android-arm64@1.0.0-rc.11':
optional: true optional: true
'@rolldown/binding-darwin-arm64@1.0.0-beta.52': '@rolldown/binding-darwin-arm64@1.0.0-rc.11':
optional: true optional: true
'@rolldown/binding-darwin-x64@1.0.0-beta.52': '@rolldown/binding-darwin-x64@1.0.0-rc.11':
optional: true optional: true
'@rolldown/binding-freebsd-x64@1.0.0-beta.52': '@rolldown/binding-freebsd-x64@1.0.0-rc.11':
optional: true optional: true
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.52': '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.11':
optional: true optional: true
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.52': '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.11':
optional: true optional: true
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.52': '@rolldown/binding-linux-arm64-musl@1.0.0-rc.11':
optional: true optional: true
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.52': '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.11':
optional: true optional: true
'@rolldown/binding-linux-x64-musl@1.0.0-beta.52': '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.11':
optional: true optional: true
'@rolldown/binding-openharmony-arm64@1.0.0-beta.52': '@rolldown/binding-linux-x64-gnu@1.0.0-rc.11':
optional: true optional: true
'@rolldown/binding-wasm32-wasi@1.0.0-beta.52': '@rolldown/binding-linux-x64-musl@1.0.0-rc.11':
optional: true
'@rolldown/binding-openharmony-arm64@1.0.0-rc.11':
optional: true
'@rolldown/binding-wasm32-wasi@1.0.0-rc.11':
dependencies: dependencies:
'@napi-rs/wasm-runtime': 1.1.1 '@napi-rs/wasm-runtime': 1.1.1
optional: true optional: true
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.52': '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.11':
optional: true optional: true
'@rolldown/binding-win32-ia32-msvc@1.0.0-beta.52': '@rolldown/binding-win32-x64-msvc@1.0.0-rc.11':
optional: true optional: true
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.52': '@rolldown/pluginutils@1.0.0-rc.11': {}
'@rspack/binding-darwin-arm64@1.7.10':
optional: true optional: true
'@rolldown/pluginutils@1.0.0-beta.52': {} '@rspack/binding-darwin-x64@1.7.10':
'@rspack/binding-darwin-arm64@1.7.9':
optional: true optional: true
'@rspack/binding-darwin-x64@1.7.9': '@rspack/binding-linux-arm64-gnu@1.7.10':
optional: true optional: true
'@rspack/binding-linux-arm64-gnu@1.7.9': '@rspack/binding-linux-arm64-musl@1.7.10':
optional: true optional: true
'@rspack/binding-linux-arm64-musl@1.7.9': '@rspack/binding-linux-x64-gnu@1.7.10':
optional: true optional: true
'@rspack/binding-linux-x64-gnu@1.7.9': '@rspack/binding-linux-x64-musl@1.7.10':
optional: true optional: true
'@rspack/binding-linux-x64-musl@1.7.9': '@rspack/binding-wasm32-wasi@1.7.10':
optional: true
'@rspack/binding-wasm32-wasi@1.7.9':
dependencies: dependencies:
'@napi-rs/wasm-runtime': 1.0.7 '@napi-rs/wasm-runtime': 1.0.7
optional: true optional: true
'@rspack/binding-win32-arm64-msvc@1.7.9': '@rspack/binding-win32-arm64-msvc@1.7.10':
optional: true optional: true
'@rspack/binding-win32-ia32-msvc@1.7.9': '@rspack/binding-win32-ia32-msvc@1.7.10':
optional: true optional: true
'@rspack/binding-win32-x64-msvc@1.7.9': '@rspack/binding-win32-x64-msvc@1.7.10':
optional: true optional: true
'@rspack/binding@1.7.9': '@rspack/binding@1.7.10':
optionalDependencies: optionalDependencies:
'@rspack/binding-darwin-arm64': 1.7.9 '@rspack/binding-darwin-arm64': 1.7.10
'@rspack/binding-darwin-x64': 1.7.9 '@rspack/binding-darwin-x64': 1.7.10
'@rspack/binding-linux-arm64-gnu': 1.7.9 '@rspack/binding-linux-arm64-gnu': 1.7.10
'@rspack/binding-linux-arm64-musl': 1.7.9 '@rspack/binding-linux-arm64-musl': 1.7.10
'@rspack/binding-linux-x64-gnu': 1.7.9 '@rspack/binding-linux-x64-gnu': 1.7.10
'@rspack/binding-linux-x64-musl': 1.7.9 '@rspack/binding-linux-x64-musl': 1.7.10
'@rspack/binding-wasm32-wasi': 1.7.9 '@rspack/binding-wasm32-wasi': 1.7.10
'@rspack/binding-win32-arm64-msvc': 1.7.9 '@rspack/binding-win32-arm64-msvc': 1.7.10
'@rspack/binding-win32-ia32-msvc': 1.7.9 '@rspack/binding-win32-ia32-msvc': 1.7.10
'@rspack/binding-win32-x64-msvc': 1.7.9 '@rspack/binding-win32-x64-msvc': 1.7.10
'@rspack/core@1.7.9': '@rspack/core@1.7.10':
dependencies: dependencies:
'@module-federation/runtime-tools': 0.22.0 '@module-federation/runtime-tools': 0.22.0
'@rspack/binding': 1.7.9 '@rspack/binding': 1.7.10
'@rspack/lite-tapable': 1.1.0 '@rspack/lite-tapable': 1.1.0
'@rspack/lite-tapable@1.1.0': {} '@rspack/lite-tapable@1.1.0': {}
@@ -6191,14 +6225,14 @@ snapshots:
ini: 1.3.8 ini: 1.3.8
proto-list: 1.2.4 proto-list: 1.2.4
cosmiconfig@9.0.1(typescript@5.9.3): cosmiconfig@9.0.1(typescript@6.0.2):
dependencies: dependencies:
env-paths: 2.2.1 env-paths: 2.2.1
import-fresh: 3.3.1 import-fresh: 3.3.1
js-yaml: 4.1.1 js-yaml: 4.1.1
parse-json: 5.2.0 parse-json: 5.2.0
optionalDependencies: optionalDependencies:
typescript: 5.9.3 typescript: 6.0.2
croner@10.0.1: {} croner@10.0.1: {}
@@ -7438,7 +7472,7 @@ snapshots:
devtools-protocol: 0.0.1581282 devtools-protocol: 0.0.1581282
typed-query-selector: 2.12.1 typed-query-selector: 2.12.1
webdriver-bidi-protocol: 0.4.1 webdriver-bidi-protocol: 0.4.1
ws: 8.19.0 ws: 8.20.0
transitivePeerDependencies: transitivePeerDependencies:
- bare-abort-controller - bare-abort-controller
- bare-buffer - bare-buffer
@@ -7447,11 +7481,11 @@ snapshots:
- supports-color - supports-color
- utf-8-validate - utf-8-validate
puppeteer@24.40.0(typescript@5.9.3): puppeteer@24.40.0(typescript@6.0.2):
dependencies: dependencies:
'@puppeteer/browsers': 2.13.0 '@puppeteer/browsers': 2.13.0
chromium-bidi: 14.0.0(devtools-protocol@0.0.1581282) chromium-bidi: 14.0.0(devtools-protocol@0.0.1581282)
cosmiconfig: 9.0.1(typescript@5.9.3) cosmiconfig: 9.0.1(typescript@6.0.2)
devtools-protocol: 0.0.1581282 devtools-protocol: 0.0.1581282
puppeteer-core: 24.40.0 puppeteer-core: 24.40.0
typed-query-selector: 2.12.1 typed-query-selector: 2.12.1
@@ -7570,25 +7604,26 @@ snapshots:
dependencies: dependencies:
glob: 7.2.3 glob: 7.2.3
rolldown@1.0.0-beta.52: rolldown@1.0.0-rc.11:
dependencies: dependencies:
'@oxc-project/types': 0.99.0 '@oxc-project/types': 0.122.0
'@rolldown/pluginutils': 1.0.0-beta.52 '@rolldown/pluginutils': 1.0.0-rc.11
optionalDependencies: optionalDependencies:
'@rolldown/binding-android-arm64': 1.0.0-beta.52 '@rolldown/binding-android-arm64': 1.0.0-rc.11
'@rolldown/binding-darwin-arm64': 1.0.0-beta.52 '@rolldown/binding-darwin-arm64': 1.0.0-rc.11
'@rolldown/binding-darwin-x64': 1.0.0-beta.52 '@rolldown/binding-darwin-x64': 1.0.0-rc.11
'@rolldown/binding-freebsd-x64': 1.0.0-beta.52 '@rolldown/binding-freebsd-x64': 1.0.0-rc.11
'@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.52 '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.11
'@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.52 '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.11
'@rolldown/binding-linux-arm64-musl': 1.0.0-beta.52 '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.11
'@rolldown/binding-linux-x64-gnu': 1.0.0-beta.52 '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.11
'@rolldown/binding-linux-x64-musl': 1.0.0-beta.52 '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.11
'@rolldown/binding-openharmony-arm64': 1.0.0-beta.52 '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.11
'@rolldown/binding-wasm32-wasi': 1.0.0-beta.52 '@rolldown/binding-linux-x64-musl': 1.0.0-rc.11
'@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.52 '@rolldown/binding-openharmony-arm64': 1.0.0-rc.11
'@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.52 '@rolldown/binding-wasm32-wasi': 1.0.0-rc.11
'@rolldown/binding-win32-x64-msvc': 1.0.0-beta.52 '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.11
'@rolldown/binding-win32-x64-msvc': 1.0.0-rc.11
run-async@3.0.0: {} run-async@3.0.0: {}
@@ -7660,7 +7695,7 @@ snapshots:
smart-buffer@4.2.0: {} smart-buffer@4.2.0: {}
smol-toml@1.6.0: {} smol-toml@1.6.1: {}
socks-proxy-agent@8.0.5: socks-proxy-agent@8.0.5:
dependencies: dependencies:
@@ -7861,7 +7896,7 @@ snapshots:
typed-query-selector@2.12.1: {} typed-query-selector@2.12.1: {}
typescript@5.9.3: {} typescript@6.0.2: {}
uglify-js@3.19.3: {} uglify-js@3.19.3: {}
@@ -7963,6 +7998,8 @@ snapshots:
ws@8.19.0: {} ws@8.19.0: {}
ws@8.20.0: {}
xml-parse-from-string@1.0.1: {} xml-parse-from-string@1.0.1: {}
xml2js@0.5.0: xml2js@0.5.0:

View File

@@ -1,20 +1,36 @@
//! Bounded, TTL-based protocol detection cache with generic failure suppression. //! Bounded, sliding-TTL protocol detection cache with periodic re-probing and failure suppression.
//! //!
//! Caches the detected protocol (H1, H2, or H3) per backend endpoint and requested //! Caches the detected protocol (H1, H2, or H3) per backend endpoint and requested
//! domain (host:port + requested_host). This prevents cache oscillation when multiple //! domain (host:port + requested_host). This prevents cache oscillation when multiple
//! frontend domains share the same backend but differ in protocol support. //! frontend domains share the same backend but differ in protocol support.
//! //!
//! ## Sliding TTL
//!
//! Each cache hit refreshes the entry's expiry timer (`last_accessed_at`). Entries
//! remain valid for up to 1 day of continuous use. Every 5 minutes, the next request
//! triggers an inline ALPN re-probe to verify the cached protocol is still correct.
//!
//! ## Upgrade signals //! ## Upgrade signals
//! //!
//! - ALPN (TLS handshake) → detects H2 vs H1 //! - ALPN (TLS handshake) → detects H2 vs H1
//! - Alt-Svc (response header) → advertises H3 //! - Alt-Svc (response header) → advertises H3
//! //!
//! ## Protocol transitions
//!
//! All protocol changes are logged at `info!()` level with the reason:
//! "Protocol transition: H1 → H2 because periodic ALPN re-probe"
//!
//! ## Failure suppression //! ## Failure suppression
//! //!
//! When a protocol fails, `record_failure()` prevents upgrade signals from //! When a protocol fails, `record_failure()` prevents upgrade signals from
//! re-introducing it until an escalating cooldown expires (5s → 10s → ... → 300s). //! re-introducing it until an escalating cooldown expires (5s → 10s → ... → 300s).
//! Within-request escalation is allowed via `can_retry()` after a 5s minimum gap. //! Within-request escalation is allowed via `can_retry()` after a 5s minimum gap.
//! //!
//! ## Total failure eviction
//!
//! When all protocols (H3, H2, H1) fail for a backend, the cache entry is evicted
//! entirely via `evict()`, forcing a fresh probe on the next request.
//!
//! Cascading: when a lower protocol also fails, higher protocol cooldowns are //! Cascading: when a lower protocol also fails, higher protocol cooldowns are
//! reduced to 5s remaining (not instant clear), preventing tight retry loops. //! reduced to 5s remaining (not instant clear), preventing tight retry loops.
@@ -22,11 +38,17 @@ use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use dashmap::DashMap; use dashmap::DashMap;
use tracing::debug; use tracing::{debug, info};
/// TTL for cached protocol detection results. /// Sliding TTL for cached protocol detection results.
/// After this duration, the next request will re-probe the backend. /// Entries that haven't been accessed for this duration are evicted.
const PROTOCOL_CACHE_TTL: Duration = Duration::from_secs(300); // 5 minutes /// Each `get()` call refreshes the timer (sliding window).
const PROTOCOL_CACHE_TTL: Duration = Duration::from_secs(86400); // 1 day
/// Interval between inline ALPN re-probes for H1/H2 entries.
/// When a cached entry's `last_probed_at` exceeds this, the next request
/// triggers an ALPN re-probe to verify the backend still speaks the same protocol.
const PROTOCOL_REPROBE_INTERVAL: Duration = Duration::from_secs(300); // 5 minutes
/// Maximum number of entries in the protocol cache. /// Maximum number of entries in the protocol cache.
const PROTOCOL_CACHE_MAX_ENTRIES: usize = 4096; const PROTOCOL_CACHE_MAX_ENTRIES: usize = 4096;
@@ -37,7 +59,7 @@ const PROTOCOL_CACHE_CLEANUP_INTERVAL: Duration = Duration::from_secs(60);
/// Minimum cooldown between retry attempts of a failed protocol. /// Minimum cooldown between retry attempts of a failed protocol.
const PROTOCOL_FAILURE_COOLDOWN: Duration = Duration::from_secs(5); const PROTOCOL_FAILURE_COOLDOWN: Duration = Duration::from_secs(5);
/// Maximum cooldown (escalation ceiling). Matches cache TTL. /// Maximum cooldown (escalation ceiling).
const PROTOCOL_FAILURE_MAX_COOLDOWN: Duration = Duration::from_secs(300); const PROTOCOL_FAILURE_MAX_COOLDOWN: Duration = Duration::from_secs(300);
/// Consecutive failure count at which cooldown reaches maximum. /// Consecutive failure count at which cooldown reaches maximum.
@@ -52,12 +74,26 @@ pub enum DetectedProtocol {
H3, H3,
} }
impl std::fmt::Display for DetectedProtocol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DetectedProtocol::H1 => write!(f, "H1"),
DetectedProtocol::H2 => write!(f, "H2"),
DetectedProtocol::H3 => write!(f, "H3"),
}
}
}
/// Result of a protocol cache lookup. /// Result of a protocol cache lookup.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct CachedProtocol { pub struct CachedProtocol {
pub protocol: DetectedProtocol, pub protocol: DetectedProtocol,
/// For H3: the port advertised by Alt-Svc (may differ from TCP port). /// For H3: the port advertised by Alt-Svc (may differ from TCP port).
pub h3_port: Option<u16>, pub h3_port: Option<u16>,
/// True if the entry's `last_probed_at` exceeds `PROTOCOL_REPROBE_INTERVAL`.
/// Caller should perform an inline ALPN re-probe and call `update_probe_result()`.
/// Always `false` for H3 entries (H3 is discovered via Alt-Svc, not ALPN).
pub needs_reprobe: bool,
} }
/// Key for the protocol cache: (host, port, requested_host). /// Key for the protocol cache: (host, port, requested_host).
@@ -70,10 +106,15 @@ pub struct ProtocolCacheKey {
pub requested_host: Option<String>, pub requested_host: Option<String>,
} }
/// A cached protocol detection result with a timestamp. /// A cached protocol detection result with timestamps.
struct CachedEntry { struct CachedEntry {
protocol: DetectedProtocol, protocol: DetectedProtocol,
/// When this protocol was first detected (or last changed).
detected_at: Instant, detected_at: Instant,
/// Last time any request used this entry (sliding-window TTL).
last_accessed_at: Instant,
/// Last time an ALPN re-probe was performed for this entry.
last_probed_at: Instant,
/// For H3: the port advertised by Alt-Svc (may differ from TCP port). /// For H3: the port advertised by Alt-Svc (may differ from TCP port).
h3_port: Option<u16>, h3_port: Option<u16>,
} }
@@ -138,6 +179,8 @@ pub struct ProtocolCacheEntry {
pub protocol: String, pub protocol: String,
pub h3_port: Option<u16>, pub h3_port: Option<u16>,
pub age_secs: u64, pub age_secs: u64,
pub last_accessed_secs: u64,
pub last_probed_secs: u64,
pub h2_suppressed: bool, pub h2_suppressed: bool,
pub h3_suppressed: bool, pub h3_suppressed: bool,
pub h2_cooldown_remaining_secs: Option<u64>, pub h2_cooldown_remaining_secs: Option<u64>,
@@ -154,11 +197,11 @@ fn escalate_cooldown(consecutive: u32) -> Duration {
Duration::from_secs(secs.min(PROTOCOL_FAILURE_MAX_COOLDOWN.as_secs())) Duration::from_secs(secs.min(PROTOCOL_FAILURE_MAX_COOLDOWN.as_secs()))
} }
/// Bounded, TTL-based protocol detection cache with failure suppression. /// Bounded, sliding-TTL protocol detection cache with failure suppression.
/// ///
/// Memory safety guarantees: /// Memory safety guarantees:
/// - Hard cap at `PROTOCOL_CACHE_MAX_ENTRIES` — cannot grow unboundedly. /// - Hard cap at `PROTOCOL_CACHE_MAX_ENTRIES` — cannot grow unboundedly.
/// - TTL expiry — stale entries naturally age out on lookup. /// - Sliding TTL expiry — entries age out after 1 day without access.
/// - Background cleanup task — proactively removes expired entries every 60s. /// - Background cleanup task — proactively removes expired entries every 60s.
/// - `clear()` — called on route updates to discard stale detections. /// - `clear()` — called on route updates to discard stale detections.
/// - `Drop` — aborts the background task to prevent dangling tokio tasks. /// - `Drop` — aborts the background task to prevent dangling tokio tasks.
@@ -190,15 +233,25 @@ impl ProtocolCache {
} }
/// Look up the cached protocol for a backend endpoint. /// Look up the cached protocol for a backend endpoint.
///
/// Returns `None` if not cached or expired (caller should probe via ALPN). /// Returns `None` if not cached or expired (caller should probe via ALPN).
/// On hit, refreshes `last_accessed_at` (sliding TTL) and sets `needs_reprobe`
/// if the entry hasn't been probed in over 5 minutes (H1/H2 only).
pub fn get(&self, key: &ProtocolCacheKey) -> Option<CachedProtocol> { pub fn get(&self, key: &ProtocolCacheKey) -> Option<CachedProtocol> {
let entry = self.cache.get(key)?; let mut entry = self.cache.get_mut(key)?;
if entry.detected_at.elapsed() < PROTOCOL_CACHE_TTL { if entry.last_accessed_at.elapsed() < PROTOCOL_CACHE_TTL {
debug!("Protocol cache hit: {:?} for {}:{} (requested: {:?})", // Refresh sliding TTL
entry.protocol, key.host, key.port, key.requested_host); entry.last_accessed_at = Instant::now();
// H3 is the ceiling — can't ALPN-probe for H3 (discovered via Alt-Svc).
// Only H1/H2 entries trigger periodic re-probing.
let needs_reprobe = entry.protocol != DetectedProtocol::H3
&& entry.last_probed_at.elapsed() >= PROTOCOL_REPROBE_INTERVAL;
Some(CachedProtocol { Some(CachedProtocol {
protocol: entry.protocol, protocol: entry.protocol,
h3_port: entry.h3_port, h3_port: entry.h3_port,
needs_reprobe,
}) })
} else { } else {
// Expired — remove and return None to trigger re-probe // Expired — remove and return None to trigger re-probe
@@ -214,7 +267,7 @@ impl ProtocolCache {
/// **Key semantic**: only suppresses if the protocol being inserted matches /// **Key semantic**: only suppresses if the protocol being inserted matches
/// a suppressed protocol. H1 inserts are NEVER suppressed — downgrades /// a suppressed protocol. H1 inserts are NEVER suppressed — downgrades
/// always succeed. /// always succeed.
pub fn insert(&self, key: ProtocolCacheKey, protocol: DetectedProtocol) -> bool { pub fn insert(&self, key: ProtocolCacheKey, protocol: DetectedProtocol, reason: &str) -> bool {
if self.is_suppressed(&key, protocol) { if self.is_suppressed(&key, protocol) {
debug!( debug!(
host = %key.host, port = %key.port, domain = ?key.requested_host, host = %key.host, port = %key.port, domain = ?key.requested_host,
@@ -223,13 +276,13 @@ impl ProtocolCache {
); );
return false; return false;
} }
self.insert_internal(key, protocol, None); self.insert_internal(key, protocol, None, reason);
true true
} }
/// Insert an H3 detection result with the Alt-Svc advertised port. /// Insert an H3 detection result with the Alt-Svc advertised port.
/// Returns `false` if H3 is suppressed. /// Returns `false` if H3 is suppressed.
pub fn insert_h3(&self, key: ProtocolCacheKey, h3_port: u16) -> bool { pub fn insert_h3(&self, key: ProtocolCacheKey, h3_port: u16, reason: &str) -> bool {
if self.is_suppressed(&key, DetectedProtocol::H3) { if self.is_suppressed(&key, DetectedProtocol::H3) {
debug!( debug!(
host = %key.host, port = %key.port, domain = ?key.requested_host, host = %key.host, port = %key.port, domain = ?key.requested_host,
@@ -237,10 +290,54 @@ impl ProtocolCache {
); );
return false; return false;
} }
self.insert_internal(key, DetectedProtocol::H3, Some(h3_port)); self.insert_internal(key, DetectedProtocol::H3, Some(h3_port), reason);
true true
} }
/// Update the cache after an inline ALPN re-probe completes.
///
/// Always updates `last_probed_at`. If the protocol changed, logs the transition
/// and updates the entry. Returns `Some(new_protocol)` if changed, `None` if unchanged.
pub fn update_probe_result(
&self,
key: &ProtocolCacheKey,
probed_protocol: DetectedProtocol,
reason: &str,
) -> Option<DetectedProtocol> {
if let Some(mut entry) = self.cache.get_mut(key) {
let old_protocol = entry.protocol;
entry.last_probed_at = Instant::now();
entry.last_accessed_at = Instant::now();
if old_protocol != probed_protocol {
info!(
host = %key.host, port = %key.port, domain = ?key.requested_host,
old = %old_protocol, new = %probed_protocol, reason = %reason,
"Protocol transition"
);
entry.protocol = probed_protocol;
entry.detected_at = Instant::now();
// Clear h3_port if downgrading from H3
if old_protocol == DetectedProtocol::H3 && probed_protocol != DetectedProtocol::H3 {
entry.h3_port = None;
}
return Some(probed_protocol);
}
debug!(
host = %key.host, port = %key.port, domain = ?key.requested_host,
protocol = %old_protocol, reason = %reason,
"Re-probe confirmed — no protocol change"
);
None
} else {
// Entry was evicted between the get() and the probe completing.
// Insert as a fresh entry.
self.insert_internal(key.clone(), probed_protocol, None, reason);
Some(probed_protocol)
}
}
/// Record a protocol failure. Future `insert()` calls for this protocol /// Record a protocol failure. Future `insert()` calls for this protocol
/// will be suppressed until the escalating cooldown expires. /// will be suppressed until the escalating cooldown expires.
/// ///
@@ -281,7 +378,7 @@ impl ProtocolCache {
Self::reduce_cooldown_to(entry.h3.as_mut(), PROTOCOL_FAILURE_COOLDOWN); Self::reduce_cooldown_to(entry.h3.as_mut(), PROTOCOL_FAILURE_COOLDOWN);
} }
debug!( info!(
host = %key.host, port = %key.port, domain = ?key.requested_host, host = %key.host, port = %key.port, domain = ?key.requested_host,
protocol = ?protocol, protocol = ?protocol,
consecutive = consecutive, consecutive = consecutive,
@@ -348,6 +445,17 @@ impl ProtocolCache {
} }
} }
/// Evict a cache entry entirely. Called when all protocol probes (H3, H2, H1)
/// have failed for a backend.
pub fn evict(&self, key: &ProtocolCacheKey) {
self.cache.remove(key);
self.failures.remove(key);
info!(
host = %key.host, port = %key.port, domain = ?key.requested_host,
"Cache entry evicted — all protocols failed"
);
}
/// Clear all entries. Called on route updates to discard stale detections. /// Clear all entries. Called on route updates to discard stale detections.
pub fn clear(&self) { pub fn clear(&self) {
self.cache.clear(); self.cache.clear();
@@ -357,7 +465,7 @@ impl ProtocolCache {
/// Snapshot all non-expired cache entries for metrics/UI display. /// Snapshot all non-expired cache entries for metrics/UI display.
pub fn snapshot(&self) -> Vec<ProtocolCacheEntry> { pub fn snapshot(&self) -> Vec<ProtocolCacheEntry> {
self.cache.iter() self.cache.iter()
.filter(|entry| entry.value().detected_at.elapsed() < PROTOCOL_CACHE_TTL) .filter(|entry| entry.value().last_accessed_at.elapsed() < PROTOCOL_CACHE_TTL)
.map(|entry| { .map(|entry| {
let key = entry.key(); let key = entry.key();
let val = entry.value(); let val = entry.value();
@@ -381,6 +489,8 @@ impl ProtocolCache {
}, },
h3_port: val.h3_port, h3_port: val.h3_port,
age_secs: val.detected_at.elapsed().as_secs(), age_secs: val.detected_at.elapsed().as_secs(),
last_accessed_secs: val.last_accessed_at.elapsed().as_secs(),
last_probed_secs: val.last_probed_at.elapsed().as_secs(),
h2_suppressed: h2_sup, h2_suppressed: h2_sup,
h3_suppressed: h3_sup, h3_suppressed: h3_sup,
h2_cooldown_remaining_secs: h2_cd, h2_cooldown_remaining_secs: h2_cd,
@@ -395,19 +505,37 @@ impl ProtocolCache {
// --- Internal helpers --- // --- Internal helpers ---
/// Insert a protocol detection result with an optional H3 port. /// Insert a protocol detection result with an optional H3 port.
/// Logs protocol transitions when overwriting an existing entry.
/// No suppression check — callers must check before calling. /// No suppression check — callers must check before calling.
fn insert_internal(&self, key: ProtocolCacheKey, protocol: DetectedProtocol, h3_port: Option<u16>) { fn insert_internal(&self, key: ProtocolCacheKey, protocol: DetectedProtocol, h3_port: Option<u16>, reason: &str) {
// Check for existing entry to log protocol transitions
if let Some(existing) = self.cache.get(&key) {
if existing.protocol != protocol {
info!(
host = %key.host, port = %key.port, domain = ?key.requested_host,
old = %existing.protocol, new = %protocol, reason = %reason,
"Protocol transition"
);
}
drop(existing);
}
// Evict oldest entry if at capacity
if self.cache.len() >= PROTOCOL_CACHE_MAX_ENTRIES && !self.cache.contains_key(&key) { if self.cache.len() >= PROTOCOL_CACHE_MAX_ENTRIES && !self.cache.contains_key(&key) {
let oldest = self.cache.iter() let oldest = self.cache.iter()
.min_by_key(|entry| entry.value().detected_at) .min_by_key(|entry| entry.value().last_accessed_at)
.map(|entry| entry.key().clone()); .map(|entry| entry.key().clone());
if let Some(oldest_key) = oldest { if let Some(oldest_key) = oldest {
self.cache.remove(&oldest_key); self.cache.remove(&oldest_key);
} }
} }
let now = Instant::now();
self.cache.insert(key, CachedEntry { self.cache.insert(key, CachedEntry {
protocol, protocol,
detected_at: Instant::now(), detected_at: now,
last_accessed_at: now,
last_probed_at: now,
h3_port, h3_port,
}); });
} }
@@ -453,9 +581,9 @@ impl ProtocolCache {
loop { loop {
interval.tick().await; interval.tick().await;
// Clean expired cache entries // Clean expired cache entries (sliding TTL based on last_accessed_at)
let expired: Vec<ProtocolCacheKey> = cache.iter() let expired: Vec<ProtocolCacheKey> = cache.iter()
.filter(|entry| entry.value().detected_at.elapsed() >= PROTOCOL_CACHE_TTL) .filter(|entry| entry.value().last_accessed_at.elapsed() >= PROTOCOL_CACHE_TTL)
.map(|entry| entry.key().clone()) .map(|entry| entry.key().clone())
.collect(); .collect();

View File

@@ -47,6 +47,8 @@ pub struct ConnActivity {
/// checks the backend's original response headers for Alt-Svc before our /// checks the backend's original response headers for Alt-Svc before our
/// ResponseFilter injects its own. None when not in auto-detect mode or after H3 failure. /// ResponseFilter injects its own. None when not in auto-detect mode or after H3 failure.
alt_svc_cache_key: Option<crate::protocol_cache::ProtocolCacheKey>, alt_svc_cache_key: Option<crate::protocol_cache::ProtocolCacheKey>,
/// The upstream request path that triggered Alt-Svc discovery. Logged for traceability.
alt_svc_request_url: Option<String>,
} }
impl ConnActivity { impl ConnActivity {
@@ -58,6 +60,7 @@ impl ConnActivity {
start: std::time::Instant::now(), start: std::time::Instant::now(),
active_requests: None, active_requests: None,
alt_svc_cache_key: None, alt_svc_cache_key: None,
alt_svc_request_url: None,
} }
} }
} }
@@ -371,7 +374,7 @@ impl HttpProxyService {
let cn = cancel_inner.clone(); let cn = cancel_inner.clone();
let la = Arc::clone(&la_inner); let la = Arc::clone(&la_inner);
let st = start; let st = start;
let ca = ConnActivity { last_activity: Arc::clone(&la_inner), start, active_requests: Some(Arc::clone(&ar_inner)), alt_svc_cache_key: None }; let ca = ConnActivity { last_activity: Arc::clone(&la_inner), start, active_requests: Some(Arc::clone(&ar_inner)), alt_svc_cache_key: None, alt_svc_request_url: None };
async move { async move {
let req = req.map(|body| BoxBody::new(body)); let req = req.map(|body| BoxBody::new(body));
let result = svc.handle_request(req, peer, port, cn, ca).await; let result = svc.handle_request(req, peer, port, cn, ca).await;
@@ -711,6 +714,9 @@ impl HttpProxyService {
let cached_h3_port = self.protocol_cache.get(&protocol_cache_key) let cached_h3_port = self.protocol_cache.get(&protocol_cache_key)
.and_then(|c| c.h3_port); .and_then(|c| c.h3_port);
// Track whether this ALPN probe is a periodic re-probe (vs first-time detection)
let mut is_reprobe = false;
let protocol_decision = match backend_protocol_mode { let protocol_decision = match backend_protocol_mode {
rustproxy_config::BackendProtocol::Http1 => ProtocolDecision::H1, rustproxy_config::BackendProtocol::Http1 => ProtocolDecision::H1,
rustproxy_config::BackendProtocol::Http2 => ProtocolDecision::H2, rustproxy_config::BackendProtocol::Http2 => ProtocolDecision::H2,
@@ -721,6 +727,12 @@ impl HttpProxyService {
ProtocolDecision::H1 ProtocolDecision::H1
} else { } else {
match self.protocol_cache.get(&protocol_cache_key) { match self.protocol_cache.get(&protocol_cache_key) {
Some(cached) if cached.needs_reprobe => {
// Entry exists but 5+ minutes since last probe — force ALPN re-probe
// (only fires for H1/H2; H3 entries have needs_reprobe=false)
is_reprobe = true;
ProtocolDecision::AlpnProbe
}
Some(cached) => match cached.protocol { Some(cached) => match cached.protocol {
crate::protocol_cache::DetectedProtocol::H3 => { crate::protocol_cache::DetectedProtocol::H3 => {
if self.protocol_cache.is_suppressed(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3) { if self.protocol_cache.is_suppressed(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3) {
@@ -766,6 +778,7 @@ impl HttpProxyService {
// the backend's original Alt-Svc header before ResponseFilter injects our own. // the backend's original Alt-Svc header before ResponseFilter injects our own.
if is_auto_detect_mode { if is_auto_detect_mode {
conn_activity.alt_svc_cache_key = Some(protocol_cache_key.clone()); conn_activity.alt_svc_cache_key = Some(protocol_cache_key.clone());
conn_activity.alt_svc_request_url = Some(upstream_path.to_string());
} }
// --- H3 path: try QUIC connection before TCP --- // --- H3 path: try QUIC connection before TCP ---
@@ -893,7 +906,7 @@ impl HttpProxyService {
let alpn = tls.get_ref().1.alpn_protocol(); let alpn = tls.get_ref().1.alpn_protocol();
let is_h2 = alpn.map(|p| p == b"h2").unwrap_or(false); let is_h2 = alpn.map(|p| p == b"h2").unwrap_or(false);
// Cache the result // Cache the result (or update existing entry for re-probes)
let cache_key = crate::protocol_cache::ProtocolCacheKey { let cache_key = crate::protocol_cache::ProtocolCacheKey {
host: upstream.host.clone(), host: upstream.host.clone(),
port: upstream.port, port: upstream.port,
@@ -904,13 +917,18 @@ impl HttpProxyService {
} else { } else {
crate::protocol_cache::DetectedProtocol::H1 crate::protocol_cache::DetectedProtocol::H1
}; };
self.protocol_cache.insert(cache_key, detected); if is_reprobe {
self.protocol_cache.update_probe_result(&cache_key, detected, "periodic ALPN re-probe");
} else {
self.protocol_cache.insert(cache_key, detected, "initial ALPN detection");
}
info!( info!(
backend = %upstream_key, backend = %upstream_key,
domain = %domain_str, domain = %domain_str,
protocol = if is_h2 { "h2" } else { "h1" }, protocol = if is_h2 { "h2" } else { "h1" },
connect_time_ms = %connect_start.elapsed().as_millis(), connect_time_ms = %connect_start.elapsed().as_millis(),
reprobe = is_reprobe,
"Backend protocol detected via ALPN" "Backend protocol detected via ALPN"
); );
@@ -938,11 +956,11 @@ impl HttpProxyService {
if let Some(h3_port) = cached_h3_port { if let Some(h3_port) = cached_h3_port {
if self.protocol_cache.can_retry(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3) { if self.protocol_cache.can_retry(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3) {
self.protocol_cache.record_retry_attempt(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3); self.protocol_cache.record_retry_attempt(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3);
debug!(backend = %upstream_key, domain = %domain_str, "TCP connect failed — escalating to H3"); debug!(backend = %upstream_key, domain = %domain_str, "TLS connect failed — escalating to H3");
match self.connect_quic_backend(&upstream.host, h3_port).await { match self.connect_quic_backend(&upstream.host, h3_port).await {
Ok(quic_conn) => { Ok(quic_conn) => {
self.protocol_cache.clear_failure(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3); self.protocol_cache.clear_failure(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3);
self.protocol_cache.insert_h3(protocol_cache_key.clone(), h3_port); self.protocol_cache.insert_h3(protocol_cache_key.clone(), h3_port, "recovery — TLS failed, H3 succeeded");
let h3_pool_key = crate::connection_pool::PoolKey { let h3_pool_key = crate::connection_pool::PoolKey {
host: upstream.host.clone(), port: h3_port, use_tls: true, host: upstream.host.clone(), port: h3_port, use_tls: true,
protocol: crate::connection_pool::PoolProtocol::H3, protocol: crate::connection_pool::PoolProtocol::H3,
@@ -961,6 +979,8 @@ impl HttpProxyService {
} }
} }
} }
// All protocols failed — evict cache entry
self.protocol_cache.evict(&protocol_cache_key);
} }
return Ok(error_response(StatusCode::BAD_GATEWAY, "Backend TLS unavailable")); return Ok(error_response(StatusCode::BAD_GATEWAY, "Backend TLS unavailable"));
} }
@@ -979,11 +999,11 @@ impl HttpProxyService {
if let Some(h3_port) = cached_h3_port { if let Some(h3_port) = cached_h3_port {
if self.protocol_cache.can_retry(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3) { if self.protocol_cache.can_retry(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3) {
self.protocol_cache.record_retry_attempt(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3); self.protocol_cache.record_retry_attempt(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3);
debug!(backend = %upstream_key, domain = %domain_str, "TCP connect timeout — escalating to H3"); debug!(backend = %upstream_key, domain = %domain_str, "TLS connect timeout — escalating to H3");
match self.connect_quic_backend(&upstream.host, h3_port).await { match self.connect_quic_backend(&upstream.host, h3_port).await {
Ok(quic_conn) => { Ok(quic_conn) => {
self.protocol_cache.clear_failure(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3); self.protocol_cache.clear_failure(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3);
self.protocol_cache.insert_h3(protocol_cache_key.clone(), h3_port); self.protocol_cache.insert_h3(protocol_cache_key.clone(), h3_port, "recovery — TLS timeout, H3 succeeded");
let h3_pool_key = crate::connection_pool::PoolKey { let h3_pool_key = crate::connection_pool::PoolKey {
host: upstream.host.clone(), port: h3_port, use_tls: true, host: upstream.host.clone(), port: h3_port, use_tls: true,
protocol: crate::connection_pool::PoolProtocol::H3, protocol: crate::connection_pool::PoolProtocol::H3,
@@ -1002,6 +1022,8 @@ impl HttpProxyService {
} }
} }
} }
// All protocols failed — evict cache entry
self.protocol_cache.evict(&protocol_cache_key);
} }
return Ok(error_response(StatusCode::GATEWAY_TIMEOUT, "Backend TLS connect timeout")); return Ok(error_response(StatusCode::GATEWAY_TIMEOUT, "Backend TLS connect timeout"));
} }
@@ -1040,7 +1062,7 @@ impl HttpProxyService {
match self.connect_quic_backend(&upstream.host, h3_port).await { match self.connect_quic_backend(&upstream.host, h3_port).await {
Ok(quic_conn) => { Ok(quic_conn) => {
self.protocol_cache.clear_failure(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3); self.protocol_cache.clear_failure(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3);
self.protocol_cache.insert_h3(protocol_cache_key.clone(), h3_port); self.protocol_cache.insert_h3(protocol_cache_key.clone(), h3_port, "recovery — TCP failed, H3 succeeded");
let h3_pool_key = crate::connection_pool::PoolKey { let h3_pool_key = crate::connection_pool::PoolKey {
host: upstream.host.clone(), port: h3_port, use_tls: true, host: upstream.host.clone(), port: h3_port, use_tls: true,
protocol: crate::connection_pool::PoolProtocol::H3, protocol: crate::connection_pool::PoolProtocol::H3,
@@ -1059,6 +1081,8 @@ impl HttpProxyService {
} }
} }
} }
// All protocols failed — evict cache entry
self.protocol_cache.evict(&protocol_cache_key);
} }
return Ok(error_response(StatusCode::BAD_GATEWAY, "Backend unavailable")); return Ok(error_response(StatusCode::BAD_GATEWAY, "Backend unavailable"));
} }
@@ -1081,7 +1105,7 @@ impl HttpProxyService {
match self.connect_quic_backend(&upstream.host, h3_port).await { match self.connect_quic_backend(&upstream.host, h3_port).await {
Ok(quic_conn) => { Ok(quic_conn) => {
self.protocol_cache.clear_failure(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3); self.protocol_cache.clear_failure(&protocol_cache_key, crate::protocol_cache::DetectedProtocol::H3);
self.protocol_cache.insert_h3(protocol_cache_key.clone(), h3_port); self.protocol_cache.insert_h3(protocol_cache_key.clone(), h3_port, "recovery — TCP timeout, H3 succeeded");
let h3_pool_key = crate::connection_pool::PoolKey { let h3_pool_key = crate::connection_pool::PoolKey {
host: upstream.host.clone(), port: h3_port, use_tls: true, host: upstream.host.clone(), port: h3_port, use_tls: true,
protocol: crate::connection_pool::PoolProtocol::H3, protocol: crate::connection_pool::PoolProtocol::H3,
@@ -1100,6 +1124,8 @@ impl HttpProxyService {
} }
} }
} }
// All protocols failed — evict cache entry
self.protocol_cache.evict(&protocol_cache_key);
} }
return Ok(error_response(StatusCode::GATEWAY_TIMEOUT, "Backend connect timeout")); return Ok(error_response(StatusCode::GATEWAY_TIMEOUT, "Backend connect timeout"));
} }
@@ -1183,8 +1209,10 @@ impl HttpProxyService {
}; };
tokio::spawn(async move { tokio::spawn(async move {
if let Err(e) = conn.await { match tokio::time::timeout(std::time::Duration::from_secs(300), conn).await {
debug!("Upstream connection error: {}", e); Ok(Err(e)) => debug!("Upstream connection error: {}", e),
Err(_) => debug!("H1 connection driver timed out after 300s"),
_ => {}
} }
}); });
@@ -1574,7 +1602,7 @@ impl HttpProxyService {
cache_key.clone(), cache_key.clone(),
crate::protocol_cache::DetectedProtocol::H2, crate::protocol_cache::DetectedProtocol::H2,
); );
self.protocol_cache.insert(cache_key, crate::protocol_cache::DetectedProtocol::H1); self.protocol_cache.insert(cache_key.clone(), crate::protocol_cache::DetectedProtocol::H1, "H2 handshake timeout — downgrade");
match self.reconnect_backend(upstream, domain, backend_key).await { match self.reconnect_backend(upstream, domain, backend_key).await {
Some(fallback_backend) => { Some(fallback_backend) => {
@@ -1593,6 +1621,8 @@ impl HttpProxyService {
result result
} }
None => { None => {
// H2 failed and H1 reconnect also failed — evict cache
self.protocol_cache.evict(&cache_key);
Ok(error_response(StatusCode::BAD_GATEWAY, "Backend unavailable after H2 timeout fallback")) Ok(error_response(StatusCode::BAD_GATEWAY, "Backend unavailable after H2 timeout fallback"))
} }
} }
@@ -1717,7 +1747,7 @@ impl HttpProxyService {
cache_key.clone(), cache_key.clone(),
crate::protocol_cache::DetectedProtocol::H2, crate::protocol_cache::DetectedProtocol::H2,
); );
self.protocol_cache.insert(cache_key, crate::protocol_cache::DetectedProtocol::H1); self.protocol_cache.insert(cache_key.clone(), crate::protocol_cache::DetectedProtocol::H1, "H2 handshake error — downgrade");
// Reconnect for H1 (the original io was consumed by the failed h2 handshake) // Reconnect for H1 (the original io was consumed by the failed h2 handshake)
match self.reconnect_backend(upstream, domain, backend_key).await { match self.reconnect_backend(upstream, domain, backend_key).await {
@@ -1738,6 +1768,8 @@ impl HttpProxyService {
result result
} }
None => { None => {
// H2 failed and H1 reconnect also failed — evict cache
self.protocol_cache.evict(&cache_key);
Ok(error_response(StatusCode::BAD_GATEWAY, "Backend unavailable after H2 fallback")) Ok(error_response(StatusCode::BAD_GATEWAY, "Backend unavailable after H2 fallback"))
} }
} }
@@ -1773,8 +1805,10 @@ impl HttpProxyService {
}; };
tokio::spawn(async move { tokio::spawn(async move {
if let Err(e) = conn.await { match tokio::time::timeout(std::time::Duration::from_secs(300), conn).await {
debug!("H1 fallback: upstream connection error: {}", e); Ok(Err(e)) => debug!("H1 fallback: upstream connection error: {}", e),
Err(_) => debug!("H1 fallback: connection driver timed out after 300s"),
_ => {}
} }
}); });
@@ -1953,8 +1987,10 @@ impl HttpProxyService {
if let Some(ref cache_key) = conn_activity.alt_svc_cache_key { if let Some(ref cache_key) = conn_activity.alt_svc_cache_key {
if let Some(alt_svc) = resp_parts.headers.get("alt-svc").and_then(|v| v.to_str().ok()) { if let Some(alt_svc) = resp_parts.headers.get("alt-svc").and_then(|v| v.to_str().ok()) {
if let Some(h3_port) = parse_alt_svc_h3_port(alt_svc) { if let Some(h3_port) = parse_alt_svc_h3_port(alt_svc) {
debug!(h3_port, "Backend advertises H3 via Alt-Svc"); let url = conn_activity.alt_svc_request_url.as_deref().unwrap_or("-");
self.protocol_cache.insert_h3(cache_key.clone(), h3_port); debug!(h3_port, url, "Backend advertises H3 via Alt-Svc");
let reason = format!("Alt-Svc response header ({})", url);
self.protocol_cache.insert_h3(cache_key.clone(), h3_port, &reason);
} }
} }
} }

View File

@@ -89,6 +89,8 @@ pub struct ProtocolCacheEntryMetric {
pub protocol: String, pub protocol: String,
pub h3_port: Option<u16>, pub h3_port: Option<u16>,
pub age_secs: u64, pub age_secs: u64,
pub last_accessed_secs: u64,
pub last_probed_secs: u64,
pub h2_suppressed: bool, pub h2_suppressed: bool,
pub h3_suppressed: bool, pub h3_suppressed: bool,
pub h2_cooldown_remaining_secs: Option<u64>, pub h2_cooldown_remaining_secs: Option<u64>,

View File

@@ -77,6 +77,13 @@ struct RelaySession {
cancel: CancellationToken, cancel: CancellationToken,
} }
impl Drop for RelaySession {
fn drop(&mut self) {
self.cancel.cancel();
self.return_task.abort();
}
}
/// Create a QUIC endpoint with a PROXY protocol v2 relay layer. /// Create a QUIC endpoint with a PROXY protocol v2 relay layer.
/// ///
/// Instead of giving the external socket to quinn, we: /// Instead of giving the external socket to quinn, we:
@@ -634,7 +641,7 @@ async fn forward_quic_stream_to_tcp(
let la_watch = Arc::clone(&last_activity); let la_watch = Arc::clone(&last_activity);
let c2b_abort = c2b.abort_handle(); let c2b_abort = c2b.abort_handle();
let b2c_abort = b2c.abort_handle(); let b2c_abort = b2c.abort_handle();
tokio::spawn(async move { let watchdog = tokio::spawn(async move {
let check_interval = std::time::Duration::from_secs(5); let check_interval = std::time::Duration::from_secs(5);
let mut last_seen = 0u64; let mut last_seen = 0u64;
loop { loop {
@@ -665,6 +672,7 @@ async fn forward_quic_stream_to_tcp(
let bytes_in = c2b.await.unwrap_or(0); let bytes_in = c2b.await.unwrap_or(0);
let bytes_out = b2c.await.unwrap_or(0); let bytes_out = b2c.await.unwrap_or(0);
watchdog.abort();
Ok((bytes_in, bytes_out)) Ok((bytes_in, bytes_out))
} }

View File

@@ -950,6 +950,8 @@ impl RustProxy {
protocol: e.protocol, protocol: e.protocol,
h3_port: e.h3_port, h3_port: e.h3_port,
age_secs: e.age_secs, age_secs: e.age_secs,
last_accessed_secs: e.last_accessed_secs,
last_probed_secs: e.last_probed_secs,
h2_suppressed: e.h2_suppressed, h2_suppressed: e.h2_suppressed,
h3_suppressed: e.h3_suppressed, h3_suppressed: e.h3_suppressed,
h2_cooldown_remaining_secs: e.h2_cooldown_remaining_secs, h2_cooldown_remaining_secs: e.h2_cooldown_remaining_secs,

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@push.rocks/smartproxy', name: '@push.rocks/smartproxy',
version: '26.1.0', version: '26.2.3',
description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.' description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.'
} }

View File

@@ -126,6 +126,8 @@ export interface IProtocolCacheEntry {
protocol: string; protocol: string;
h3Port: number | null; h3Port: number | null;
ageSecs: number; ageSecs: number;
lastAccessedSecs: number;
lastProbedSecs: number;
h2Suppressed: boolean; h2Suppressed: boolean;
h3Suppressed: boolean; h3Suppressed: boolean;
h2CooldownRemainingSecs: number | null; h2CooldownRemainingSecs: number | null;

View File

@@ -69,14 +69,15 @@ export function createOffsetPortMappingRoute(options: {
priority?: number; priority?: number;
[key: string]: any; [key: string]: any;
}): IRouteConfig { }): IRouteConfig {
const { ports, targetHost, offset, name, domains, priority, ...rest } = options;
return createPortMappingRoute({ return createPortMappingRoute({
sourcePortRange: options.ports, sourcePortRange: ports,
targetHost: options.targetHost, targetHost,
portMapper: (context) => context.port + options.offset, portMapper: (context) => context.port + offset,
name: options.name || `Offset Mapping (${options.offset > 0 ? '+' : ''}${options.offset}) for ${options.domains || 'all domains'}`, name: name || `Offset Mapping (${offset > 0 ? '+' : ''}${offset}) for ${domains || 'all domains'}`,
domains: options.domains, domains,
priority: options.priority, priority,
...options ...rest
}); });
} }

View File

@@ -258,7 +258,9 @@ export class RouteValidator {
errorMap.set(route.name, existingErrors); errorMap.set(route.name, existingErrors);
valid = false; valid = false;
} }
routeNames.add(route.name); if (route.name) {
routeNames.add(route.name);
}
} }
// Validate each route // Validate each route
@@ -328,7 +330,7 @@ export class RouteValidator {
if (catchAllRoutes.length > 1) { if (catchAllRoutes.length > 1) {
for (const route of catchAllRoutes) { for (const route of catchAllRoutes) {
conflicts.push({ conflicts.push({
route: route.name, route: route.name || 'unnamed',
message: `Multiple catch-all routes on port ${port}` message: `Multiple catch-all routes on port ${port}`
}); });
} }

View File

@@ -7,8 +7,7 @@
"moduleResolution": "NodeNext", "moduleResolution": "NodeNext",
"esModuleInterop": true, "esModuleInterop": true,
"verbatimModuleSyntax": true, "verbatimModuleSyntax": true,
"baseUrl": ".", "ignoreDeprecations": "6.0"
"paths": {}
}, },
"exclude": [ "exclude": [
"dist_*/**/*.d.ts" "dist_*/**/*.d.ts"