feat(smarts3): Add local smarts3 testing support and documentation
This commit is contained in:
@@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-11-24 - 1.8.0 - feat(smarts3)
|
||||||
|
Add local smarts3 testing support and documentation
|
||||||
|
|
||||||
|
- Added @push.rocks/smarts3 ^5.1.0 to devDependencies to enable a local S3-compatible test server.
|
||||||
|
- Updated README with a new "Testing with smarts3" section including a Quick Start example and integration test commands.
|
||||||
|
- Documented benefits and CI-friendly usage for running registry integration tests locally without cloud credentials.
|
||||||
|
|
||||||
## 2025-11-23 - 1.7.0 - feat(core)
|
## 2025-11-23 - 1.7.0 - feat(core)
|
||||||
Standardize S3 storage config using @tsclass/tsclass IS3Descriptor and wire it into RegistryStorage and plugins exports; update README and package dependencies.
|
Standardize S3 storage config using @tsclass/tsclass IS3Descriptor and wire it into RegistryStorage and plugins exports; update README and package dependencies.
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"@git.zone/tsbundle": "^2.0.5",
|
"@git.zone/tsbundle": "^2.0.5",
|
||||||
"@git.zone/tsrun": "^2.0.0",
|
"@git.zone/tsrun": "^2.0.0",
|
||||||
"@git.zone/tstest": "^3.1.0",
|
"@git.zone/tstest": "^3.1.0",
|
||||||
|
"@push.rocks/smarts3": "^5.1.0",
|
||||||
"@types/node": "^24.10.1"
|
"@types/node": "^24.10.1"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
58
pnpm-lock.yaml
generated
58
pnpm-lock.yaml
generated
@@ -39,6 +39,9 @@ importers:
|
|||||||
'@git.zone/tstest':
|
'@git.zone/tstest':
|
||||||
specifier: ^3.1.0
|
specifier: ^3.1.0
|
||||||
version: 3.1.0(socks@2.8.7)(typescript@5.9.3)
|
version: 3.1.0(socks@2.8.7)(typescript@5.9.3)
|
||||||
|
'@push.rocks/smarts3':
|
||||||
|
specifier: ^5.1.0
|
||||||
|
version: 5.1.0
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^24.10.1
|
specifier: ^24.10.1
|
||||||
version: 24.10.1
|
version: 24.10.1
|
||||||
@@ -576,7 +579,6 @@ packages:
|
|||||||
'@koa/router@9.4.0':
|
'@koa/router@9.4.0':
|
||||||
resolution: {integrity: sha512-dOOXgzqaDoHu5qqMEPLKEgLz5CeIA7q8+1W62mCvFVCOqeC71UoTGJ4u1xUSOpIl2J1x2pqrNULkFteUeZW3/A==}
|
resolution: {integrity: sha512-dOOXgzqaDoHu5qqMEPLKEgLz5CeIA7q8+1W62mCvFVCOqeC71UoTGJ4u1xUSOpIl2J1x2pqrNULkFteUeZW3/A==}
|
||||||
engines: {node: '>= 8.0.0'}
|
engines: {node: '>= 8.0.0'}
|
||||||
deprecated: '**IMPORTANT 10x+ PERFORMANCE UPGRADE**: Please upgrade to v12.0.1+ as we have fixed an issue with debuglog causing 10x slower router benchmark performance, see https://github.com/koajs/router/pull/173'
|
|
||||||
|
|
||||||
'@leichtgewicht/ip-codec@2.0.5':
|
'@leichtgewicht/ip-codec@2.0.5':
|
||||||
resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==}
|
resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==}
|
||||||
@@ -763,6 +765,9 @@ packages:
|
|||||||
'@push.rocks/smartfile@11.2.7':
|
'@push.rocks/smartfile@11.2.7':
|
||||||
resolution: {integrity: sha512-8Yp7/sAgPpWJBHohV92ogHWKzRomI5MEbSG6b5W2n18tqwfAmjMed0rQvsvGrSBlnEWCKgoOrYIIZbLO61+J0Q==}
|
resolution: {integrity: sha512-8Yp7/sAgPpWJBHohV92ogHWKzRomI5MEbSG6b5W2n18tqwfAmjMed0rQvsvGrSBlnEWCKgoOrYIIZbLO61+J0Q==}
|
||||||
|
|
||||||
|
'@push.rocks/smartfs@1.1.0':
|
||||||
|
resolution: {integrity: sha512-fg8JIjFUPPX5laRoBpTaGwhMfZ3Y8mFT4fUaW54Y4J/BfOBa/y0+rIFgvgvqcOZgkQlyZU+FIfL8Z6zezqxyTg==}
|
||||||
|
|
||||||
'@push.rocks/smartguard@3.1.0':
|
'@push.rocks/smartguard@3.1.0':
|
||||||
resolution: {integrity: sha512-J23q84f1O+TwFGmd4lrO9XLHUh2DaLXo9PN/9VmTWYzTkQDv5JehmifXVI0esophXcCIfbdIu6hbt7/aHlDF4A==}
|
resolution: {integrity: sha512-J23q84f1O+TwFGmd4lrO9XLHUh2DaLXo9PN/9VmTWYzTkQDv5JehmifXVI0esophXcCIfbdIu6hbt7/aHlDF4A==}
|
||||||
|
|
||||||
@@ -850,6 +855,9 @@ packages:
|
|||||||
'@push.rocks/smarts3@2.2.7':
|
'@push.rocks/smarts3@2.2.7':
|
||||||
resolution: {integrity: sha512-9ZXGMlmUL2Wd+YJO0xOB8KyqPf4V++fWJvTq4s76bnqEuaCr9OLfq6czhban+i4cD3ZdIjehfuHqctzjuLw8Jw==}
|
resolution: {integrity: sha512-9ZXGMlmUL2Wd+YJO0xOB8KyqPf4V++fWJvTq4s76bnqEuaCr9OLfq6czhban+i4cD3ZdIjehfuHqctzjuLw8Jw==}
|
||||||
|
|
||||||
|
'@push.rocks/smarts3@5.1.0':
|
||||||
|
resolution: {integrity: sha512-jmoSaJkdWOWxiS5aiTXvE6+zS7n6+OZe1jxIOq3weX54tPmDCjpLLTl12rdgvvpDE1ai5ayftirWhLGk96hkaw==}
|
||||||
|
|
||||||
'@push.rocks/smartshell@3.3.0':
|
'@push.rocks/smartshell@3.3.0':
|
||||||
resolution: {integrity: sha512-m0w618H6YBs+vXGz1CgS4nPi5CUAnqRtckcS9/koGwfcIx1IpjqmiP47BoCTbdgcv0IPUxQVBG1IXTHPuZ8Z5g==}
|
resolution: {integrity: sha512-m0w618H6YBs+vXGz1CgS4nPi5CUAnqRtckcS9/koGwfcIx1IpjqmiP47BoCTbdgcv0IPUxQVBG1IXTHPuZ8Z5g==}
|
||||||
|
|
||||||
@@ -1754,7 +1762,7 @@ packages:
|
|||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
co@4.6.0:
|
co@4.6.0:
|
||||||
resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
|
resolution: {integrity: sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=}
|
||||||
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
|
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
|
||||||
|
|
||||||
color-convert@1.9.3:
|
color-convert@1.9.3:
|
||||||
@@ -1769,7 +1777,7 @@ packages:
|
|||||||
engines: {node: '>=14.6'}
|
engines: {node: '>=14.6'}
|
||||||
|
|
||||||
color-name@1.1.3:
|
color-name@1.1.3:
|
||||||
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
|
resolution: {integrity: sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=}
|
||||||
|
|
||||||
color-name@1.1.4:
|
color-name@1.1.4:
|
||||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||||
@@ -1894,7 +1902,7 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
deep-equal@1.0.1:
|
deep-equal@1.0.1:
|
||||||
resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==}
|
resolution: {integrity: sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=}
|
||||||
|
|
||||||
deep-extend@0.6.0:
|
deep-extend@0.6.0:
|
||||||
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
|
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
|
||||||
@@ -1925,10 +1933,10 @@ packages:
|
|||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
|
|
||||||
delegates@1.0.0:
|
delegates@1.0.0:
|
||||||
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
|
resolution: {integrity: sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=}
|
||||||
|
|
||||||
depd@1.1.2:
|
depd@1.1.2:
|
||||||
resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==}
|
resolution: {integrity: sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
depd@2.0.0:
|
depd@2.0.0:
|
||||||
@@ -1980,7 +1988,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==}
|
resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==}
|
||||||
|
|
||||||
encodeurl@1.0.2:
|
encodeurl@1.0.2:
|
||||||
resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
|
resolution: {integrity: sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
encodeurl@2.0.0:
|
encodeurl@2.0.0:
|
||||||
@@ -2041,7 +2049,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
|
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
|
||||||
|
|
||||||
escape-string-regexp@1.0.5:
|
escape-string-regexp@1.0.5:
|
||||||
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
|
resolution: {integrity: sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=}
|
||||||
engines: {node: '>=0.8.0'}
|
engines: {node: '>=0.8.0'}
|
||||||
|
|
||||||
escape-string-regexp@5.0.0:
|
escape-string-regexp@5.0.0:
|
||||||
@@ -2202,7 +2210,7 @@ packages:
|
|||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
fresh@0.5.2:
|
fresh@0.5.2:
|
||||||
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
|
resolution: {integrity: sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
fresh@2.0.0:
|
fresh@2.0.0:
|
||||||
@@ -2291,7 +2299,7 @@ packages:
|
|||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
has-flag@3.0.0:
|
has-flag@3.0.0:
|
||||||
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
|
resolution: {integrity: sha1-tdRU3CGZriJWmfNGfloH87lVuv0=}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
has-property-descriptors@1.0.2:
|
has-property-descriptors@1.0.2:
|
||||||
@@ -2367,7 +2375,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
|
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
|
||||||
|
|
||||||
humanize-number@0.0.2:
|
humanize-number@0.0.2:
|
||||||
resolution: {integrity: sha512-un3ZAcNQGI7RzaWGZzQDH47HETM4Wrj6z6E4TId8Yeq9w5ZKUVB1nrT2jwFheTUjEmqcgTjXDc959jum+ai1kQ==}
|
resolution: {integrity: sha1-EcCvakcWQ2M1iFiASPF5lUFInBg=}
|
||||||
|
|
||||||
iconv-lite@0.6.3:
|
iconv-lite@0.6.3:
|
||||||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||||
@@ -2496,7 +2504,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
|
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
|
||||||
|
|
||||||
jsonfile@4.0.0:
|
jsonfile@4.0.0:
|
||||||
resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
|
resolution: {integrity: sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=}
|
||||||
|
|
||||||
jsonfile@6.2.0:
|
jsonfile@6.2.0:
|
||||||
resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==}
|
resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==}
|
||||||
@@ -2504,7 +2512,6 @@ packages:
|
|||||||
keygrip@1.1.0:
|
keygrip@1.1.0:
|
||||||
resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==}
|
resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
|
|
||||||
|
|
||||||
keyv@4.5.4:
|
keyv@4.5.4:
|
||||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||||
@@ -2686,7 +2693,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
|
resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
|
||||||
|
|
||||||
media-typer@0.3.0:
|
media-typer@0.3.0:
|
||||||
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
|
resolution: {integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
media-typer@1.1.0:
|
media-typer@1.1.0:
|
||||||
@@ -2701,7 +2708,7 @@ packages:
|
|||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
methods@1.1.2:
|
methods@1.1.2:
|
||||||
resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
|
resolution: {integrity: sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
micromark-core-commonmark@2.0.3:
|
micromark-core-commonmark@2.0.3:
|
||||||
@@ -2954,7 +2961,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==}
|
resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==}
|
||||||
|
|
||||||
only@0.0.2:
|
only@0.0.2:
|
||||||
resolution: {integrity: sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==}
|
resolution: {integrity: sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q=}
|
||||||
|
|
||||||
open@8.4.2:
|
open@8.4.2:
|
||||||
resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
|
resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
|
||||||
@@ -3026,7 +3033,7 @@ packages:
|
|||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
passthrough-counter@1.0.0:
|
passthrough-counter@1.0.0:
|
||||||
resolution: {integrity: sha512-Wy8PXTLqPAN0oEgBrlnsXPMww3SYJ44tQ8aVrGAI4h4JZYCS0oYqsPqtPR8OhJpv6qFbpbB7XAn0liKV7EXubA==}
|
resolution: {integrity: sha1-GWfZ5m2lcrXAI8eH2xEqOHqxZvo=}
|
||||||
|
|
||||||
path-exists@4.0.0:
|
path-exists@4.0.0:
|
||||||
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
||||||
@@ -3352,10 +3359,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
|
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
|
||||||
|
|
||||||
stack-trace@0.0.10:
|
stack-trace@0.0.10:
|
||||||
resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
|
resolution: {integrity: sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=}
|
||||||
|
|
||||||
statuses@1.5.0:
|
statuses@1.5.0:
|
||||||
resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
|
resolution: {integrity: sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
statuses@2.0.1:
|
statuses@2.0.1:
|
||||||
@@ -3367,7 +3374,7 @@ packages:
|
|||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
streamsearch@0.1.2:
|
streamsearch@0.1.2:
|
||||||
resolution: {integrity: sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==}
|
resolution: {integrity: sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=}
|
||||||
engines: {node: '>=0.8.0'}
|
engines: {node: '>=0.8.0'}
|
||||||
|
|
||||||
streamx@2.23.0:
|
streamx@2.23.0:
|
||||||
@@ -5446,6 +5453,10 @@ snapshots:
|
|||||||
glob: 11.1.0
|
glob: 11.1.0
|
||||||
js-yaml: 4.1.1
|
js-yaml: 4.1.1
|
||||||
|
|
||||||
|
'@push.rocks/smartfs@1.1.0':
|
||||||
|
dependencies:
|
||||||
|
'@push.rocks/smartpath': 6.0.0
|
||||||
|
|
||||||
'@push.rocks/smartguard@3.1.0':
|
'@push.rocks/smartguard@3.1.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@push.rocks/smartpromise': 4.2.3
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
@@ -5694,6 +5705,13 @@ snapshots:
|
|||||||
- aws-crt
|
- aws-crt
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
'@push.rocks/smarts3@5.1.0':
|
||||||
|
dependencies:
|
||||||
|
'@push.rocks/smartfs': 1.1.0
|
||||||
|
'@push.rocks/smartpath': 6.0.0
|
||||||
|
'@push.rocks/smartxml': 2.0.0
|
||||||
|
'@tsclass/tsclass': 9.3.0
|
||||||
|
|
||||||
'@push.rocks/smartshell@3.3.0':
|
'@push.rocks/smartshell@3.3.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@push.rocks/smartdelay': 3.0.5
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
|
|||||||
76
readme.md
76
readme.md
@@ -1024,6 +1024,82 @@ pnpm run build
|
|||||||
pnpm test
|
pnpm test
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 🧪 Testing with smarts3
|
||||||
|
|
||||||
|
smartregistry works seamlessly with [@push.rocks/smarts3](https://code.foss.global/push.rocks/smarts3), a local S3-compatible server for testing. This allows you to test the registry without needing cloud credentials or external services.
|
||||||
|
|
||||||
|
### Quick Start with smarts3
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { Smarts3 } from '@push.rocks/smarts3';
|
||||||
|
import { SmartRegistry } from '@push.rocks/smartregistry';
|
||||||
|
|
||||||
|
// Start local S3 server
|
||||||
|
const s3Server = await Smarts3.createAndStart({
|
||||||
|
server: { port: 3456 },
|
||||||
|
storage: { cleanSlate: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Manually create IS3Descriptor matching smarts3 configuration
|
||||||
|
// Note: smarts3 v5.1.0 doesn't properly expose getS3Descriptor() yet
|
||||||
|
const s3Descriptor = {
|
||||||
|
endpoint: 'localhost',
|
||||||
|
port: 3456,
|
||||||
|
accessKey: 'test',
|
||||||
|
accessSecret: 'test',
|
||||||
|
useSsl: false,
|
||||||
|
region: 'us-east-1',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create registry with smarts3 configuration
|
||||||
|
const registry = new SmartRegistry({
|
||||||
|
storage: {
|
||||||
|
...s3Descriptor,
|
||||||
|
bucketName: 'my-test-registry',
|
||||||
|
},
|
||||||
|
auth: {
|
||||||
|
jwtSecret: 'test-secret',
|
||||||
|
tokenStore: 'memory',
|
||||||
|
npmTokens: { enabled: true },
|
||||||
|
ociTokens: {
|
||||||
|
enabled: true,
|
||||||
|
realm: 'https://auth.example.com/token',
|
||||||
|
service: 'my-registry',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
npm: { enabled: true, basePath: '/npm' },
|
||||||
|
oci: { enabled: true, basePath: '/oci' },
|
||||||
|
pypi: { enabled: true, basePath: '/pypi' },
|
||||||
|
cargo: { enabled: true, basePath: '/cargo' },
|
||||||
|
});
|
||||||
|
|
||||||
|
await registry.init();
|
||||||
|
|
||||||
|
// Use registry...
|
||||||
|
// Your tests here
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
await s3Server.stop();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Benefits of Testing with smarts3
|
||||||
|
|
||||||
|
- ✅ **Zero Setup** - No cloud credentials or external services needed
|
||||||
|
- ✅ **Fast** - Local filesystem storage, no network latency
|
||||||
|
- ✅ **Isolated** - Clean slate per test run, no shared state
|
||||||
|
- ✅ **CI/CD Ready** - Works in automated pipelines without configuration
|
||||||
|
- ✅ **Full Compatibility** - Implements S3 API, works with IS3Descriptor
|
||||||
|
|
||||||
|
### Running Integration Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run smarts3 integration test
|
||||||
|
pnpm exec tstest test/test.integration.smarts3.node.ts --verbose
|
||||||
|
|
||||||
|
# Run all tests (includes smarts3)
|
||||||
|
pnpm test
|
||||||
|
```
|
||||||
|
|
||||||
## License and Legal Information
|
## License and Legal Information
|
||||||
|
|
||||||
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
||||||
|
|||||||
291
test/test.integration.smarts3.node.ts
Normal file
291
test/test.integration.smarts3.node.ts
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
/**
|
||||||
|
* Integration test for smartregistry with smarts3
|
||||||
|
* Verifies that smartregistry works with a local S3-compatible server
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||||
|
import * as smarts3Module from '@push.rocks/smarts3';
|
||||||
|
import { SmartRegistry } from '../ts/classes.smartregistry.js';
|
||||||
|
import type { IRegistryConfig } from '../ts/core/interfaces.core.js';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
|
|
||||||
|
let s3Server: smarts3Module.Smarts3;
|
||||||
|
let registry: SmartRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup: Start smarts3 server
|
||||||
|
*/
|
||||||
|
tap.test('should start smarts3 server', async () => {
|
||||||
|
s3Server = await smarts3Module.Smarts3.createAndStart({
|
||||||
|
server: {
|
||||||
|
port: 3456, // Use different port to avoid conflicts with other tests
|
||||||
|
host: '0.0.0.0',
|
||||||
|
},
|
||||||
|
storage: {
|
||||||
|
cleanSlate: true, // Fresh storage for each test run
|
||||||
|
bucketsDir: './.nogit/smarts3-test-buckets',
|
||||||
|
},
|
||||||
|
logging: {
|
||||||
|
silent: true, // Reduce test output noise
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(s3Server).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup: Create SmartRegistry with smarts3 configuration
|
||||||
|
*/
|
||||||
|
tap.test('should create SmartRegistry instance with smarts3 IS3Descriptor', async () => {
|
||||||
|
// Manually construct IS3Descriptor based on smarts3 configuration
|
||||||
|
// Note: smarts3.getS3Descriptor() returns empty object as of v5.1.0
|
||||||
|
// This is a known limitation - smarts3 doesn't expose its config properly
|
||||||
|
const s3Descriptor = {
|
||||||
|
endpoint: 'localhost',
|
||||||
|
port: 3456,
|
||||||
|
accessKey: 'test', // smarts3 doesn't require real credentials
|
||||||
|
accessSecret: 'test',
|
||||||
|
useSsl: false,
|
||||||
|
region: 'us-east-1',
|
||||||
|
};
|
||||||
|
|
||||||
|
const config: IRegistryConfig = {
|
||||||
|
storage: {
|
||||||
|
...s3Descriptor,
|
||||||
|
bucketName: 'test-registry-smarts3',
|
||||||
|
},
|
||||||
|
auth: {
|
||||||
|
jwtSecret: 'test-secret-key',
|
||||||
|
tokenStore: 'memory',
|
||||||
|
npmTokens: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
ociTokens: {
|
||||||
|
enabled: true,
|
||||||
|
realm: 'https://auth.example.com/token',
|
||||||
|
service: 'test-registry-smarts3',
|
||||||
|
},
|
||||||
|
pypiTokens: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
rubygemsTokens: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
npm: {
|
||||||
|
enabled: true,
|
||||||
|
basePath: '/npm',
|
||||||
|
},
|
||||||
|
oci: {
|
||||||
|
enabled: true,
|
||||||
|
basePath: '/oci',
|
||||||
|
},
|
||||||
|
pypi: {
|
||||||
|
enabled: true,
|
||||||
|
basePath: '/pypi',
|
||||||
|
},
|
||||||
|
cargo: {
|
||||||
|
enabled: true,
|
||||||
|
basePath: '/cargo',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
registry = new SmartRegistry(config);
|
||||||
|
await registry.init();
|
||||||
|
|
||||||
|
expect(registry).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test NPM protocol with smarts3
|
||||||
|
*/
|
||||||
|
tap.test('NPM: should publish package to smarts3', async () => {
|
||||||
|
const authManager = registry.getAuthManager();
|
||||||
|
const userId = await authManager.authenticate({
|
||||||
|
username: 'testuser',
|
||||||
|
password: 'testpass',
|
||||||
|
});
|
||||||
|
const token = await authManager.createNpmToken(userId, false);
|
||||||
|
|
||||||
|
const packageData = {
|
||||||
|
name: 'test-package-smarts3',
|
||||||
|
'dist-tags': {
|
||||||
|
latest: '1.0.0',
|
||||||
|
},
|
||||||
|
versions: {
|
||||||
|
'1.0.0': {
|
||||||
|
name: 'test-package-smarts3',
|
||||||
|
version: '1.0.0',
|
||||||
|
description: 'Test package for smarts3 integration',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_attachments: {
|
||||||
|
'test-package-smarts3-1.0.0.tgz': {
|
||||||
|
content_type: 'application/octet-stream',
|
||||||
|
data: Buffer.from('test tarball content').toString('base64'),
|
||||||
|
length: 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await registry.handleRequest({
|
||||||
|
method: 'PUT',
|
||||||
|
path: '/npm/test-package-smarts3',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
query: {},
|
||||||
|
body: packageData,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toEqual(201); // 201 Created is correct for publishing
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('NPM: should retrieve package from smarts3', async () => {
|
||||||
|
const response = await registry.handleRequest({
|
||||||
|
method: 'GET',
|
||||||
|
path: '/npm/test-package-smarts3',
|
||||||
|
headers: {},
|
||||||
|
query: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200);
|
||||||
|
expect(response.body).toHaveProperty('name');
|
||||||
|
expect(response.body.name).toEqual('test-package-smarts3');
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test OCI protocol with smarts3
|
||||||
|
*/
|
||||||
|
tap.test('OCI: should store blob in smarts3', async () => {
|
||||||
|
const authManager = registry.getAuthManager();
|
||||||
|
const userId = await authManager.authenticate({
|
||||||
|
username: 'testuser',
|
||||||
|
password: 'testpass',
|
||||||
|
});
|
||||||
|
const token = await authManager.createOciToken(
|
||||||
|
userId,
|
||||||
|
['oci:repository:test-image:push'],
|
||||||
|
3600
|
||||||
|
);
|
||||||
|
|
||||||
|
// Initiate blob upload
|
||||||
|
const initiateResponse = await registry.handleRequest({
|
||||||
|
method: 'POST',
|
||||||
|
path: '/oci/v2/test-image/blobs/uploads/',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
query: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(initiateResponse.status).toEqual(202);
|
||||||
|
expect(initiateResponse.headers).toHaveProperty('Location');
|
||||||
|
|
||||||
|
// Extract upload ID from location
|
||||||
|
const location = initiateResponse.headers['Location'];
|
||||||
|
const uploadId = location.split('/').pop();
|
||||||
|
|
||||||
|
// Upload blob data
|
||||||
|
const blobData = Buffer.from('test blob content');
|
||||||
|
const digest = 'sha256:' + crypto
|
||||||
|
.createHash('sha256')
|
||||||
|
.update(blobData)
|
||||||
|
.digest('hex');
|
||||||
|
|
||||||
|
const uploadResponse = await registry.handleRequest({
|
||||||
|
method: 'PUT',
|
||||||
|
path: `/oci/v2/test-image/blobs/uploads/${uploadId}`,
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`,
|
||||||
|
'Content-Type': 'application/octet-stream',
|
||||||
|
},
|
||||||
|
query: { digest },
|
||||||
|
body: blobData,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(uploadResponse.status).toEqual(201);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test PyPI protocol with smarts3
|
||||||
|
*/
|
||||||
|
tap.test('PyPI: should upload package to smarts3', async () => {
|
||||||
|
const authManager = registry.getAuthManager();
|
||||||
|
const userId = await authManager.authenticate({
|
||||||
|
username: 'testuser',
|
||||||
|
password: 'testpass',
|
||||||
|
});
|
||||||
|
const token = await authManager.createPypiToken(userId, false);
|
||||||
|
|
||||||
|
// Note: In a real test, this would be multipart/form-data
|
||||||
|
// For simplicity, we're testing the storage layer
|
||||||
|
const storage = registry.getStorage();
|
||||||
|
|
||||||
|
// Store a test package file
|
||||||
|
const packageContent = Buffer.from('test wheel content');
|
||||||
|
await storage.putPypiPackageFile(
|
||||||
|
'test-package',
|
||||||
|
'test_package-1.0.0-py3-none-any.whl',
|
||||||
|
packageContent
|
||||||
|
);
|
||||||
|
|
||||||
|
// Store metadata
|
||||||
|
const metadata = {
|
||||||
|
name: 'test-package',
|
||||||
|
version: '1.0.0',
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
filename: 'test_package-1.0.0-py3-none-any.whl',
|
||||||
|
url: '/packages/test-package/test_package-1.0.0-py3-none-any.whl',
|
||||||
|
hashes: { sha256: 'abc123' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
await storage.putPypiPackageMetadata('test-package', metadata);
|
||||||
|
|
||||||
|
// Verify stored
|
||||||
|
const retrievedMetadata = await storage.getPypiPackageMetadata('test-package');
|
||||||
|
expect(retrievedMetadata).toBeDefined();
|
||||||
|
expect(retrievedMetadata.name).toEqual('test-package');
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Cargo protocol with smarts3
|
||||||
|
*/
|
||||||
|
tap.test('Cargo: should store crate in smarts3', async () => {
|
||||||
|
const storage = registry.getStorage();
|
||||||
|
|
||||||
|
// Store a test crate index entry
|
||||||
|
const indexEntry = {
|
||||||
|
name: 'test-crate',
|
||||||
|
vers: '1.0.0',
|
||||||
|
deps: [],
|
||||||
|
cksum: 'abc123',
|
||||||
|
features: {},
|
||||||
|
yanked: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
await storage.putCargoIndex('test-crate', [indexEntry]);
|
||||||
|
|
||||||
|
// Store the actual .crate file
|
||||||
|
const crateContent = Buffer.from('test crate tarball');
|
||||||
|
await storage.putCargoCrate('test-crate', '1.0.0', crateContent);
|
||||||
|
|
||||||
|
// Verify stored
|
||||||
|
const retrievedIndex = await storage.getCargoIndex('test-crate');
|
||||||
|
expect(retrievedIndex).toBeDefined();
|
||||||
|
expect(retrievedIndex.length).toEqual(1);
|
||||||
|
expect(retrievedIndex[0].name).toEqual('test-crate');
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup: Stop smarts3 server
|
||||||
|
*/
|
||||||
|
tap.test('should stop smarts3 server', async () => {
|
||||||
|
await s3Server.stop();
|
||||||
|
expect(true).toEqual(true); // Just verify it completes without error
|
||||||
|
});
|
||||||
|
|
||||||
|
export default tap.start();
|
||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@push.rocks/smartregistry',
|
name: '@push.rocks/smartregistry',
|
||||||
version: '1.7.0',
|
version: '1.8.0',
|
||||||
description: 'A composable TypeScript library implementing OCI, NPM, Maven, Cargo, Composer, PyPI, and RubyGems registries for building unified container and package registries'
|
description: 'A composable TypeScript library implementing OCI, NPM, Maven, Cargo, Composer, PyPI, and RubyGems registries for building unified container and package registries'
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user