Compare commits

..

6 Commits

11 changed files with 316 additions and 192 deletions

View File

@@ -1,5 +1,21 @@
# Changelog
## 2025-08-19 - 1.3.4 - fix(test)
Increase test timeout, enable DockerImageStore test, update test image name, bump smartrequest patch, and add local claude settings
- Increase tstest timeout from 120s to 600s in package.json to accommodate longer-running integration tests.
- Unskip the DockerImageStore integration test and change stored image name from 'hello' to 'hello2' in test/test.nonci.node.ts.
- Bump dependency @push.rocks/smartrequest from ^4.3.0 to ^4.3.1.
- Add .claude/settings.local.json to allow local agent permissions for running tests and related tooling.
## 2025-08-19 - 1.3.3 - fix(classes.host)
Adjust requestStreaming timeout and autoDrain; stabilize tests
- Reduced requestStreaming timeout from 10 minutes to 30 seconds to avoid long-running hanging requests.
- Enabled autoDrain for streaming requests to ensure response streams are properly drained and reduce resource issues.
- Marked the DockerImageStore S3 integration test as skipped to avoid CI dependence on external S3 and added a cleanup test to stop the test DockerHost.
- Added local tool settings file (.claude/settings.local.json) with local permissions (development-only).
## 2025-08-18 - 1.3.2 - fix(package.json)
Fix test script timeout typo, update dependency versions, and add typings & project configs

View File

@@ -1,13 +1,13 @@
{
"name": "@apiclient.xyz/docker",
"version": "1.3.2",
"version": "1.3.4",
"description": "Provides easy communication with Docker remote API from Node.js, with TypeScript support.",
"private": false,
"main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts",
"type": "module",
"scripts": {
"test": "(tstest test/ --verbose --logfile --timeout 120)",
"test": "(tstest test/ --verbose --logfile --timeout 600)",
"build": "(tsbuild --web --allowimplicitany)",
"buildDocs": "tsdoc"
},
@@ -39,15 +39,15 @@
"@push.rocks/smartfile": "^11.2.7",
"@push.rocks/smartjson": "^5.0.20",
"@push.rocks/smartlog": "^3.1.8",
"@push.rocks/smartnetwork": "^3.0.0",
"@push.rocks/smartpath": "^5.0.18",
"@push.rocks/smartnetwork": "^4.1.2",
"@push.rocks/smartpath": "^6.0.0",
"@push.rocks/smartpromise": "^4.2.3",
"@push.rocks/smartrequest": "^2.0.22",
"@push.rocks/smartrequest": "^4.3.1",
"@push.rocks/smartstream": "^3.2.5",
"@push.rocks/smartstring": "^4.0.15",
"@push.rocks/smartunique": "^3.0.9",
"@push.rocks/smartversion": "^3.0.5",
"@tsclass/tsclass": "^4.1.2",
"@tsclass/tsclass": "^9.2.0",
"rxjs": "^7.8.2"
},
"devDependencies": {

172
pnpm-lock.yaml generated
View File

@@ -27,17 +27,17 @@ importers:
specifier: ^3.1.8
version: 3.1.8
'@push.rocks/smartnetwork':
specifier: ^3.0.0
version: 3.0.2
specifier: ^4.1.2
version: 4.1.2
'@push.rocks/smartpath':
specifier: ^5.0.18
version: 5.0.18
specifier: ^6.0.0
version: 6.0.0
'@push.rocks/smartpromise':
specifier: ^4.2.3
version: 4.2.3
'@push.rocks/smartrequest':
specifier: ^2.0.22
version: 2.0.22
specifier: ^4.3.1
version: 4.3.1
'@push.rocks/smartstream':
specifier: ^3.2.5
version: 3.2.5
@@ -51,8 +51,8 @@ importers:
specifier: ^3.0.5
version: 3.0.5
'@tsclass/tsclass':
specifier: ^4.1.2
version: 4.1.2
specifier: ^9.2.0
version: 9.2.0
rxjs:
specifier: ^7.8.2
version: 7.8.2
@@ -679,9 +679,6 @@ packages:
'@push.rocks/smartmongo@2.0.12':
resolution: {integrity: sha512-NglYiO14BikxnlvW6JF18FtopBtaWQEGAtPxHmmSCbyhU8Mi0aEFO7VgCasE9Kguba/wcR597qhcDEdcpBg1eQ==}
'@push.rocks/smartnetwork@3.0.2':
resolution: {integrity: sha512-s6CNGzQ1n/d/6cOKXbxeW6/tO//dr1woLqI01g7XhqTriw0nsm2G2kWaZh2J0VOguGNWBgQVCIpR0LjdRNWb3g==}
'@push.rocks/smartnetwork@4.1.2':
resolution: {integrity: sha512-TjucG72ooHgzAUpNu2LAv4iFoettmZq2aEWhhzIa7AKcOvt4yxsk3Vl73guhKRohTfhdRauPcH5OHISLUHJbYA==}
@@ -697,8 +694,8 @@ packages:
'@push.rocks/smartopen@2.0.0':
resolution: {integrity: sha512-eVT0GhtQ2drb95j/kktYst/Toh1zCwCqjTJFYtaYFUnnBnBUajPtBZDFnPQo01DN8JxoeCTo8jggq+PCvzcfww==}
'@push.rocks/smartpath@5.0.18':
resolution: {integrity: sha512-kIyRTlOoeEth5b4Qp8KPUxNOGNdvhb2aD0hbHfF3oGTQ0xnDdgB1l03/4bIoapHG48OrTgh4uQ5tUorykgdOzw==}
'@push.rocks/smartpath@5.1.0':
resolution: {integrity: sha512-pJ4UGATHV/C6Dw5DU0D3MJaPMASlKAgeS+Hl9dkhD2ceYArn86Ky3Z/g7LNj40Oz6cUe77/AP1chztmJZISrpw==}
'@push.rocks/smartpath@6.0.0':
resolution: {integrity: sha512-r94u1MbBaIOSy+517PZp2P7SuZPSe9LkwJ8l3dXQKHeIOri/zDxk/RQPiFM+j4N9301ztkRyhvRj7xgUDroOsg==}
@@ -715,11 +712,11 @@ packages:
'@push.rocks/smartpuppeteer@2.0.5':
resolution: {integrity: sha512-yK/qSeWVHIGWRp3c8S5tfdGP6WCKllZC4DR8d8CQlEjszOSBmHtlTdyyqOMBZ/BA4kd+eU5f3A1r4K2tGYty1g==}
'@push.rocks/smartrequest@2.0.22':
resolution: {integrity: sha512-EfgmdEsLtDJ8aNOLOh59ca1NMsiiFz54aoHRigQFQ0cuoUs6phxejIY2FdMoPFn68ubTpkztdL2P4L1/cRYyHg==}
'@push.rocks/smartrequest@2.1.0':
resolution: {integrity: sha512-3eHLTRInHA+u+W98TqJwgTES7rRimBAsJC4JxVNQC3UUezmblAhM5/TIQsEBQTsbjAY8SeQKy6NHzW6iTiaD8w==}
'@push.rocks/smartrequest@4.2.2':
resolution: {integrity: sha512-OMtSyxvuOw04nRR/97yAF3XLe42QDyuwStQMo2bXrVvXgcriNyn0JFavl0IL5hY6Gb+dyCWgOAUwX7cVZ4DzZg==}
'@push.rocks/smartrequest@4.3.1':
resolution: {integrity: sha512-H5FQSfFEbSJCHpE2A+SasQQcxM5FlxhHIUEzhUsSLjtlCTEu9T7Xb1WzVLFYvdWfyP5VIrg+XM4AMOols8cG+Q==}
'@push.rocks/smartrouter@1.3.3':
resolution: {integrity: sha512-1+xZEnWlhzqLWAaJ1zFNhQ0zgbfCWQl1DBT72LygLxTs+P0K8AwJKgqo/IX6CT55kGCFnPAZIYSbVJlGsgrB0w==}
@@ -757,9 +754,6 @@ packages:
'@push.rocks/smartunique@3.0.9':
resolution: {integrity: sha512-q6DYQgT7/dqdWi9HusvtWCjdsFzLFXY9LTtaZV6IYNJt6teZOonoygxTdNt9XLn6niBSbLYrHSKvJNTRH/uK+g==}
'@push.rocks/smarturl@3.0.7':
resolution: {integrity: sha512-nx4EWjQD9JeO7QVbOsxd1PFeDQYoSQOOOYCZ+r7QWXHLJG52iYzgvJDCQyX6p705HDkYMJWozW2ZzhR22qLKbw==}
'@push.rocks/smarturl@3.1.0':
resolution: {integrity: sha512-ij73Q4GERojdPSHxAvYKvspimcpAJC6GGQCWsC4b+1sAiOSByjfmkUHK8yiEEOPRU9AeGuyaIVqK6ZzKLEZ3vA==}
@@ -823,10 +817,6 @@ packages:
resolution: {integrity: sha512-PLvBNVeuY9BERNLq3PFDkhnHHc0RpilEGHd4aUI5XRFlZF++LETdLxPbxw+DHbvHlkUf/nep09f7rrL9Tqub1Q==}
deprecated: This package has been deprecated in favour of the new package at @push.rocks/smartmatch
'@pushrocks/smartping@1.0.8':
resolution: {integrity: sha512-VM2gfS1sTuycj/jHyDa0lDntkPe7/JT0b2kGsy265RkichAJZkoEp3fboRJH/WAdzM8T4Du64JYgZkc8v2HHQg==}
deprecated: This package has been deprecated in favour of the new package at @push.rocks/smartping
'@pushrocks/smartpromise@3.1.10':
resolution: {integrity: sha512-VeTurbZ1+ZMxBDJk1Y1LV8SN9xLI+oDXKVeCFw41FAGEKOUEqordqFpi6t+7Vhe/TXUZzCVpZ5bXxAxrGf8yTQ==}
deprecated: This package has been deprecated in favour of the new package at @push.rocks/smartpromise
@@ -1227,8 +1217,8 @@ packages:
'@tsclass/tsclass@3.0.48':
resolution: {integrity: sha512-hC65UvDlp9qvsl6OcIZXz0JNiWZ0gyzsTzbXpg215sGxopgbkOLCr6E0s4qCTnweYm95gt2AdY95uP7M7kExaQ==}
'@tsclass/tsclass@4.1.2':
resolution: {integrity: sha512-uMg1IcTU1cP0McXYGwGffoU3asNQHle7bTN0tn6kVXzfNzSwQf4o8v+YQ4VRnUzo4ov6VKcPXqg5OLb2vz977g==}
'@tsclass/tsclass@4.4.4':
resolution: {integrity: sha512-YZOAF+u+r4u5rCev2uUd1KBTBdfyFdtDmcv4wuN+864lMccbdfRICR3SlJwCfYS1lbeV3QNLYGD30wjRXgvCJA==}
'@tsclass/tsclass@9.2.0':
resolution: {integrity: sha512-A6ULEkQfYgOnCKQVQRt26O7PRzFo4PE2EoD25RAtnuFuVrNwGynYC20Vee2c8KAOyI7nQ/LaREki9KAX4AHOHQ==}
@@ -1254,9 +1244,6 @@ packages:
'@types/debug@4.1.12':
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
'@types/default-gateway@3.0.1':
resolution: {integrity: sha512-tpu0hp+AOIzwdAHyZPzLE5pCf9uT0pb+xZ76T4S7MrY2YTVq918Q7Q2VQ3KCVQqYxM7nxuCK/SL3X97jBEIeKQ==}
'@types/default-gateway@7.2.2':
resolution: {integrity: sha512-35C93fYQlnLKLASkMPoxRvok4fENwB3By9clRLd2I/08n/XRl0pCdf7EB17K5oMMwZu8NBYA8i66jH5r/LYBKA==}
@@ -1403,10 +1390,6 @@ packages:
resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==}
engines: {node: '>= 8.0.0'}
aggregate-error@4.0.1:
resolution: {integrity: sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==}
engines: {node: '>=12'}
ansi-256-colors@1.1.0:
resolution: {integrity: sha1-kQ3lDvzHwJ49gvL4er1rcAwYgYo=}
engines: {node: '>=0.10.0'}
@@ -1609,10 +1592,6 @@ packages:
resolution: {integrity: sha1-noIVAa6XmYbEax1m0tQy2y/UrjE=}
engines: {node: '>=4'}
clean-stack@4.2.0:
resolution: {integrity: sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==}
engines: {node: '>=12'}
cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
@@ -2063,10 +2042,6 @@ packages:
resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==}
engines: {node: '>= 14.17'}
form-data@4.0.0:
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
engines: {node: '>= 6'}
form-data@4.0.4:
resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
engines: {node: '>= 6'}
@@ -2270,10 +2245,6 @@ packages:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
indent-string@5.0.0:
resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==}
engines: {node: '>=12'}
inflight@1.0.6:
resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=}
@@ -2314,10 +2285,6 @@ packages:
resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==}
engines: {node: '>= 0.4'}
is-ip@4.0.0:
resolution: {integrity: sha512-4B4XA2HEIm/PY+OSpeMBXr8pGWBYbXuHgjMAqrwbLO3CPTCAd9ArEJzBUKGZtk9viY6+aSfadGnWyjY3ydYZkw==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
is-ip@5.0.1:
resolution: {integrity: sha512-FCsGHdlrOnZQcp0+XT5a+pYowf33itBalCl+7ovNXC/7o5BhIpG14M3OrpPPdBSIQJCm+0M5+9mO7S9VVTTCFw==}
engines: {node: '>=14.16'}
@@ -3023,10 +2990,6 @@ packages:
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
public-ip@6.0.2:
resolution: {integrity: sha512-+6bkjnf0yQ4+tZV0zJv1017DiIF7y6R4yg17Mrhhkc25L7dtQtXWHgSCrz9BbLL4OeTFbPK4EALXqJUrwCIWXw==}
engines: {node: '>=14.16'}
public-ip@7.0.1:
resolution: {integrity: sha512-DdNcqcIbI0wEeCBcqX+bmZpUCvrDMJHXE553zgyG1MZ8S1a/iCCxmK9iTjjql+SpHSv4cZkmRv5/zGYW93AlCw==}
engines: {node: '>=18'}
@@ -3369,12 +3332,6 @@ packages:
symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
systeminformation@5.22.8:
resolution: {integrity: sha512-F1iWQ+PSfOzvLMGh2UXASaWLDq5o+1h1db13Kddl6ojcQ47rsJhpMtRrmBXfTA5QJgutC4KV67YRmXLuroIxrA==}
engines: {node: '>=8.0.0'}
os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
hasBin: true
systeminformation@5.27.7:
resolution: {integrity: sha512-saaqOoVEEFaux4v0K8Q7caiauRwjXC4XbD2eH60dxHXbpKxQ8kH9Rf7Jh+nryKpOUSEFxtCdBlSUx0/lO6rwRg==}
engines: {node: '>=8.0.0'}
@@ -3460,10 +3417,6 @@ packages:
resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
engines: {node: '>=12.20'}
type-fest@4.25.0:
resolution: {integrity: sha512-bRkIGlXsnGBRBQRAY56UXBm//9qH4bmJfFvq83gSz41N282df+fjy8ofcEgc1sM8geNt5cl6mC2g9Fht1cs8Aw==}
engines: {node: '>=16'}
type-fest@4.41.0:
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
engines: {node: '>=16'}
@@ -3713,7 +3666,7 @@ snapshots:
'@push.rocks/smartopen': 2.0.0
'@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrequest': 4.2.2
'@push.rocks/smartrequest': 4.3.1
'@push.rocks/smartrx': 3.0.10
'@push.rocks/smartsitemap': 2.0.3
'@push.rocks/smartstream': 3.2.5
@@ -4410,7 +4363,7 @@ snapshots:
'@push.rocks/smartfile': 11.2.7
'@push.rocks/smartlog': 3.1.8
'@push.rocks/smartlog-destination-local': 9.0.2
'@push.rocks/smartpath': 5.0.18
'@push.rocks/smartpath': 5.1.0
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartspawn': 3.0.3
'@rspack/core': 1.4.11
@@ -4432,7 +4385,7 @@ snapshots:
'@push.rocks/smartlog': 3.1.8
'@push.rocks/smartnpm': 2.0.6
'@push.rocks/smartpath': 6.0.0
'@push.rocks/smartrequest': 4.2.2
'@push.rocks/smartrequest': 4.3.1
'@push.rocks/smartshell': 3.3.0
transitivePeerDependencies:
- aws-crt
@@ -4463,7 +4416,7 @@ snapshots:
'@push.rocks/smartmongo': 2.0.12(socks@2.8.7)
'@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrequest': 4.2.2
'@push.rocks/smartrequest': 4.3.1
'@push.rocks/smarts3': 2.2.6
'@push.rocks/smartshell': 3.3.0
'@push.rocks/smarttime': 4.1.1
@@ -4643,12 +4596,12 @@ snapshots:
'@push.rocks/smartexit': 1.0.23
'@push.rocks/smartfile': 11.2.7
'@push.rocks/smartjson': 5.0.20
'@push.rocks/smartpath': 5.0.18
'@push.rocks/smartpath': 5.1.0
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartstring': 4.0.15
'@push.rocks/smartunique': 3.0.9
'@push.rocks/taskbuffer': 3.1.7
'@tsclass/tsclass': 4.1.2
'@tsclass/tsclass': 4.4.4
transitivePeerDependencies:
- aws-crt
@@ -4695,7 +4648,7 @@ snapshots:
'@push.rocks/smartfile': 11.2.7
'@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrequest': 4.2.2
'@push.rocks/smartrequest': 4.3.1
'@push.rocks/smartrx': 3.0.10
'@push.rocks/smartstream': 3.2.5
'@push.rocks/smartunique': 3.0.9
@@ -4846,7 +4799,7 @@ snapshots:
'@push.rocks/smartmime': 2.0.4
'@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrequest': 4.2.2
'@push.rocks/smartrequest': 4.3.1
'@push.rocks/smartstream': 3.2.5
'@types/fs-extra': 11.0.4
'@types/js-yaml': 4.0.9
@@ -4857,7 +4810,7 @@ snapshots:
'@push.rocks/smartguard@3.1.0':
dependencies:
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrequest': 2.0.22
'@push.rocks/smartrequest': 2.1.0
'@push.rocks/smarthash@3.2.3':
dependencies:
@@ -4887,7 +4840,7 @@ snapshots:
'@push.rocks/smartlog-interfaces@3.0.2':
dependencies:
'@api.global/typedrequest-interfaces': 2.0.2
'@tsclass/tsclass': 4.1.2
'@tsclass/tsclass': 4.4.4
'@push.rocks/smartlog@3.1.8':
dependencies:
@@ -4933,7 +4886,7 @@ snapshots:
dependencies:
'@push.rocks/mongodump': 1.1.0(socks@2.8.7)
'@push.rocks/smartdata': 5.16.4(socks@2.8.7)
'@push.rocks/smartpath': 5.0.18
'@push.rocks/smartpath': 5.1.0
'@push.rocks/smartpromise': 4.2.3
mongodb-memory-server: 10.2.0(socks@2.8.7)
transitivePeerDependencies:
@@ -4946,16 +4899,6 @@ snapshots:
- socks
- supports-color
'@push.rocks/smartnetwork@3.0.2':
dependencies:
'@pushrocks/smartping': 1.0.8
'@pushrocks/smartpromise': 3.1.10
'@pushrocks/smartstring': 4.0.7
'@types/default-gateway': 3.0.1
isopen: 1.3.0
public-ip: 6.0.2
systeminformation: 5.22.8
'@push.rocks/smartnetwork@4.1.2':
dependencies:
'@push.rocks/smartping': 1.0.8
@@ -4974,7 +4917,7 @@ snapshots:
'@push.rocks/smartfile': 11.2.7
'@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrequest': 4.2.2
'@push.rocks/smartrequest': 4.3.1
'@push.rocks/smarttime': 4.1.1
'@push.rocks/smartversion': 3.0.5
package-json: 8.1.1
@@ -5003,7 +4946,7 @@ snapshots:
dependencies:
open: 8.4.2
'@push.rocks/smartpath@5.0.18': {}
'@push.rocks/smartpath@5.1.0': {}
'@push.rocks/smartpath@6.0.0': {}
@@ -5049,14 +4992,14 @@ snapshots:
- typescript
- utf-8-validate
'@push.rocks/smartrequest@2.0.22':
'@push.rocks/smartrequest@2.1.0':
dependencies:
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smarturl': 3.0.7
'@push.rocks/smarturl': 3.1.0
agentkeepalive: 4.5.0
form-data: 4.0.0
form-data: 4.0.4
'@push.rocks/smartrequest@4.2.2':
'@push.rocks/smartrequest@4.3.1':
dependencies:
'@push.rocks/smartenv': 5.0.13
'@push.rocks/smartpath': 6.0.0
@@ -5104,7 +5047,7 @@ snapshots:
'@push.rocks/smartxml': 1.1.1
'@push.rocks/smartyaml': 2.0.5
'@push.rocks/webrequest': 3.0.37
'@tsclass/tsclass': 4.1.2
'@tsclass/tsclass': 4.4.4
'@push.rocks/smartsocket@2.1.0':
dependencies:
@@ -5184,8 +5127,6 @@ snapshots:
nanoid: 4.0.2
uuid: 9.0.1
'@push.rocks/smarturl@3.0.7': {}
'@push.rocks/smarturl@3.1.0': {}
'@push.rocks/smartversion@3.0.5':
@@ -5225,7 +5166,7 @@ snapshots:
dependencies:
'@pushrocks/smartdelay': 3.0.1
'@pushrocks/smartpromise': 4.0.2
'@tsclass/tsclass': 4.1.2
'@tsclass/tsclass': 4.4.4
'@push.rocks/webstore@2.0.20':
dependencies:
@@ -5291,11 +5232,6 @@ snapshots:
dependencies:
matcher: 5.0.0
'@pushrocks/smartping@1.0.8':
dependencies:
'@types/ping': 0.4.4
ping: 0.4.4
'@pushrocks/smartpromise@3.1.10': {}
'@pushrocks/smartpromise@4.0.2': {}
@@ -5791,9 +5727,9 @@ snapshots:
dependencies:
type-fest: 2.19.0
'@tsclass/tsclass@4.1.2':
'@tsclass/tsclass@4.4.4':
dependencies:
type-fest: 4.25.0
type-fest: 4.41.0
'@tsclass/tsclass@9.2.0':
dependencies:
@@ -5828,8 +5764,6 @@ snapshots:
dependencies:
'@types/ms': 2.1.0
'@types/default-gateway@3.0.1': {}
'@types/default-gateway@7.2.2': {}
'@types/express-serve-static-core@5.0.7':
@@ -5982,11 +5916,6 @@ snapshots:
dependencies:
humanize-ms: 1.2.1
aggregate-error@4.0.1:
dependencies:
clean-stack: 4.2.0
indent-string: 5.0.0
ansi-256-colors@1.1.0: {}
ansi-regex@5.0.1: {}
@@ -6183,10 +6112,6 @@ snapshots:
clean-stack@1.3.0: {}
clean-stack@4.2.0:
dependencies:
escape-string-regexp: 5.0.0
cliui@8.0.1:
dependencies:
string-width: 4.2.3
@@ -6658,12 +6583,6 @@ snapshots:
form-data-encoder@2.1.4: {}
form-data@4.0.0:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
form-data@4.0.4:
dependencies:
asynckit: 0.4.0
@@ -6931,8 +6850,6 @@ snapshots:
parent-module: 1.0.1
resolve-from: 4.0.0
indent-string@5.0.0: {}
inflight@1.0.6:
dependencies:
once: 1.4.0
@@ -6963,10 +6880,6 @@ snapshots:
has-tostringtag: 1.0.2
safe-regex-test: 1.1.0
is-ip@4.0.0:
dependencies:
ip-regex: 5.0.0
is-ip@5.0.1:
dependencies:
ip-regex: 5.0.0
@@ -7830,13 +7743,6 @@ snapshots:
proxy-from-env@1.1.0: {}
public-ip@6.0.2:
dependencies:
aggregate-error: 4.0.1
dns-socket: 4.2.2
got: 12.6.1
is-ip: 4.0.0
public-ip@7.0.1:
dependencies:
dns-socket: 4.2.2
@@ -8310,8 +8216,6 @@ snapshots:
symbol-tree@3.2.4: {}
systeminformation@5.22.8: {}
systeminformation@5.27.7: {}
tar-fs@3.1.0:
@@ -8402,8 +8306,6 @@ snapshots:
type-fest@2.19.0: {}
type-fest@4.25.0: {}
type-fest@4.41.0: {}
type-is@1.6.18:

40
test-stream.js Normal file
View File

@@ -0,0 +1,40 @@
const { SmartRequest } = require('@push.rocks/smartrequest');
async function test() {
try {
const response = await SmartRequest.create()
.url('http://unix:/run/user/1000/docker.sock:/images/hello-world:latest/get')
.header('Host', 'docker.sock')
.get();
console.log('Response status:', response.status);
console.log('Response type:', typeof response);
const stream = response.streamNode();
console.log('Stream type:', typeof stream);
console.log('Has on method:', typeof stream.on);
if (stream) {
let chunks = 0;
stream.on('data', (chunk) => {
chunks++;
if (chunks <= 3) console.log('Got chunk', chunks, chunk.length);
});
stream.on('end', () => {
console.log('Stream ended, total chunks:', chunks);
process.exit(0);
});
stream.on('error', (err) => {
console.error('Stream error:', err);
process.exit(1);
});
} else {
console.log('No stream available');
}
} catch (error) {
console.error('Error:', error);
process.exit(1);
}
}
test();

46
test-stream.mjs Normal file
View File

@@ -0,0 +1,46 @@
import { SmartRequest } from '@push.rocks/smartrequest';
async function test() {
try {
const response = await SmartRequest.create()
.url('http://unix:/run/user/1000/docker.sock:/images/hello-world:latest/get')
.header('Host', 'docker.sock')
.get();
console.log('Response status:', response.status);
console.log('Response type:', typeof response);
const stream = response.streamNode();
console.log('Stream type:', typeof stream);
console.log('Has on method:', typeof stream.on);
if (stream) {
let chunks = 0;
stream.on('data', (chunk) => {
chunks++;
if (chunks <= 3) console.log('Got chunk', chunks, chunk.length);
});
stream.on('end', () => {
console.log('Stream ended, total chunks:', chunks);
process.exit(0);
});
stream.on('error', (err) => {
console.error('Stream error:', err);
process.exit(1);
});
// Set a timeout in case stream doesn't end
setTimeout(() => {
console.log('Timeout after 5 seconds');
process.exit(1);
}, 5000);
} else {
console.log('No stream available');
}
} catch (error) {
console.error('Error:', error);
process.exit(1);
}
}
test();

View File

@@ -139,17 +139,17 @@ tap.test('should export images', async (toolsArg) => {
await done.promise;
});
tap.test('should import images', async (toolsArg) => {
const done = toolsArg.defer();
tap.test('should import images', async () => {
const fsReadStream = plugins.smartfile.fsStream.createReadStream(
plugins.path.join(paths.nogitDir, 'testimage.tar')
);
await docker.DockerImage.createFromTarStream(testDockerHost, {
const importedImage = await docker.DockerImage.createFromTarStream(testDockerHost, {
tarStream: fsReadStream,
creationObject: {
imageUrl: 'code.foss.global/host.today/ht-docker-node:latest',
}
})
});
expect(importedImage).toBeInstanceOf(docker.DockerImage);
});
tap.test('should expose a working DockerImageStore', async () => {
@@ -163,7 +163,11 @@ tap.test('should expose a working DockerImageStore', async () => {
await testDockerHost.addS3Storage(s3Descriptor);
//
await testDockerHost.imageStore.storeImage('hello', plugins.smartfile.fsStream.createReadStream(plugins.path.join(paths.nogitDir, 'testimage.tar')));
await testDockerHost.imageStore.storeImage('hello2', plugins.smartfile.fsStream.createReadStream(plugins.path.join(paths.nogitDir, 'testimage.tar')));
})
tap.test('cleanup', async () => {
await testDockerHost.stop();
})
export default tap.start();

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@apiclient.xyz/docker',
version: '1.3.2',
version: '1.3.4',
description: 'Provides easy communication with Docker remote API from Node.js, with TypeScript support.'
}

View File

@@ -226,38 +226,91 @@ export class DockerHost {
*/
public async request(methodArg: string, routeArg: string, dataArg = {}) {
const requestUrl = `${this.socketPath}${routeArg}`;
const response = await plugins.smartrequest.request(requestUrl, {
method: methodArg,
headers: {
'Content-Type': 'application/json',
'X-Registry-Auth': this.registryToken,
Host: 'docker.sock',
},
requestBody: dataArg,
keepAlive: false,
});
if (response.statusCode !== 200) {
console.log(response.body);
// Build the request using the fluent API
const smartRequest = plugins.smartrequest.SmartRequest.create()
.url(requestUrl)
.header('Content-Type', 'application/json')
.header('X-Registry-Auth', this.registryToken)
.header('Host', 'docker.sock')
.options({ keepAlive: false });
// Add body for methods that support it
if (dataArg && Object.keys(dataArg).length > 0) {
smartRequest.json(dataArg);
}
return response;
// Execute the request based on method
let response;
switch (methodArg.toUpperCase()) {
case 'GET':
response = await smartRequest.get();
break;
case 'POST':
response = await smartRequest.post();
break;
case 'PUT':
response = await smartRequest.put();
break;
case 'DELETE':
response = await smartRequest.delete();
break;
default:
throw new Error(`Unsupported HTTP method: ${methodArg}`);
}
// Parse the response body based on content type
let body;
const contentType = response.headers['content-type'] || '';
// Docker's streaming endpoints (like /images/create) return newline-delimited JSON
// which can't be parsed as a single JSON object
const isStreamingEndpoint = routeArg.includes('/images/create') ||
routeArg.includes('/images/load') ||
routeArg.includes('/build');
if (contentType.includes('application/json') && !isStreamingEndpoint) {
body = await response.json();
} else {
body = await response.text();
// Try to parse as JSON if it looks like JSON and is not a streaming response
if (!isStreamingEndpoint && body && (body.startsWith('{') || body.startsWith('['))) {
try {
body = JSON.parse(body);
} catch {
// Keep as text if parsing fails
}
}
}
// Create a response object compatible with existing code
const legacyResponse = {
statusCode: response.status,
body: body,
headers: response.headers
};
if (response.status !== 200) {
console.log(body);
}
return legacyResponse;
}
public async requestStreaming(methodArg: string, routeArg: string, readStream?: plugins.smartstream.stream.Readable) {
const requestUrl = `${this.socketPath}${routeArg}`;
const response = await plugins.smartrequest.request(
requestUrl,
{
method: methodArg,
headers: {
'Content-Type': 'application/json',
'X-Registry-Auth': this.registryToken,
Host: 'docker.sock',
},
requestBody: null,
keepAlive: false,
},
true,
(readStream ? reqArg => {
// Build the request using the fluent API
const smartRequest = plugins.smartrequest.SmartRequest.create()
.url(requestUrl)
.header('Content-Type', 'application/json')
.header('X-Registry-Auth', this.registryToken)
.header('Host', 'docker.sock')
.timeout(30000)
.options({ keepAlive: false, autoDrain: true }); // Disable auto-drain for streaming
// If we have a readStream, use the new stream method with logging
if (readStream) {
let counter = 0;
const smartduplex = new plugins.smartstream.SmartDuplex({
writeFunction: async (chunkArg) => {
@@ -268,12 +321,56 @@ export class DockerHost {
return chunkArg;
}
});
readStream.pipe(smartduplex).pipe(reqArg);
} : null),
);
console.log(response.statusCode);
console.log(response.body);
return response;
// Pipe through the logging duplex stream
const loggedStream = readStream.pipe(smartduplex);
// Use the new stream method to stream the data
smartRequest.stream(loggedStream, 'application/octet-stream');
}
// Execute the request based on method
let response;
switch (methodArg.toUpperCase()) {
case 'GET':
response = await smartRequest.get();
break;
case 'POST':
response = await smartRequest.post();
break;
case 'PUT':
response = await smartRequest.put();
break;
case 'DELETE':
response = await smartRequest.delete();
break;
default:
throw new Error(`Unsupported HTTP method: ${methodArg}`);
}
console.log(response.status);
// For streaming responses, get the Node.js stream
const nodeStream = response.streamNode();
if (!nodeStream) {
// If no stream is available, consume the body as text
const body = await response.text();
console.log(body);
// Return a compatible response object
return {
statusCode: response.status,
body: body,
headers: response.headers
};
}
// For streaming responses, return the stream with added properties
(nodeStream as any).statusCode = response.status;
(nodeStream as any).body = ''; // For compatibility
return nodeStream;
}
/**

View File

@@ -250,6 +250,12 @@ export class DockerImage {
public async exportToTarStream(): Promise<plugins.smartstream.stream.Readable> {
logger.log('info', `Exporting image ${this.RepoTags[0]} to tar stream.`);
const response = await this.dockerHost.requestStreaming('GET', `/images/${encodeURIComponent(this.RepoTags[0])}/get`);
// Check if response is a Node.js stream
if (!response || typeof response.on !== 'function') {
throw new Error('Failed to get streaming response for image export');
}
let counter = 0;
const webduplexStream = new plugins.smartstream.SmartDuplex({
writeFunction: async (chunk, tools) => {
@@ -259,17 +265,25 @@ export class DockerImage {
return chunk;
}
});
response.on('data', (chunk) => {
if (!webduplexStream.write(chunk)) {
response.pause();
webduplexStream.once('drain', () => {
response.resume();
})
};
});
}
});
response.on('end', () => {
webduplexStream.end();
})
});
response.on('error', (error) => {
logger.log('error', `Error during image export: ${error.message}`);
webduplexStream.destroy(error);
});
return webduplexStream;
}
}

View File

@@ -89,6 +89,11 @@ export class DockerService {
}> = [];
for (const network of serviceCreationDescriptor.networks) {
// Skip null networks (can happen if network creation fails)
if (!network) {
logger.log('warn', 'Skipping null network in service creation');
continue;
}
networkArray.push({
Target: network.Name,
Aliases: [serviceCreationDescriptor.networkAlias],