Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bc71d2e5a8 | |||
| 0cf48b3688 | |||
| 1305b92ebe | |||
| 8b52ca1021 | |||
| e14800f077 | |||
| 9f3503704b | |||
| f3ba77050a | |||
| 6211acd60b |
37
changelog.md
37
changelog.md
@@ -1,5 +1,42 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-11-17 - 5.0.0 - BREAKING CHANGE(client/streaming)
|
||||
Unify streaming APIs: remove raw()/streamNode() and standardize on web ReadableStream across runtimes
|
||||
|
||||
- Removed SmartRequest.raw() and RawStreamFunction type. The raw streaming function API is gone — use .stream() with a web ReadableStream for request body streaming.
|
||||
- Removed response.streamNode() from all runtimes. Responses now expose only response.stream() (ReadableStream<Uint8Array>). Node.js consumers must convert using Readable.fromWeb() if a Node.js stream is required.
|
||||
- Node implementation now uses Readable.toWeb() to convert native Node streams into web ReadableStream for a single cross-platform streaming API.
|
||||
- Client request.stream() still accepts Node.js streams but they are converted internally to web streams; temporary internal properties for raw streaming were removed.
|
||||
- Updated tests and documentation (readme) with migration guidance and examples for converting between web and Node.js streams.
|
||||
- Bumped devDependencies (@git.zone/tsbuild, tsrun, tstest) and upgraded form-data to a newer patch release.
|
||||
|
||||
## 2025-11-16 - 4.4.2 - fix(core_base/request)
|
||||
Strip 'unix:' prefix when parsing unix socket URLs so socketPath is a clean filesystem path
|
||||
|
||||
- CoreRequest.parseUnixSocketUrl now removes a leading 'unix:' prefix and returns socketPath as a filesystem path (e.g., /var/run/docker.sock)
|
||||
- Updated tests for Bun, Deno and Node to expect socketPath without the 'unix:' prefix
|
||||
- Adjusted comments/documentation in core_base/request.ts to clarify returned socketPath format
|
||||
|
||||
## 2025-11-16 - 4.4.1 - fix(core_node)
|
||||
Fix unix socket URL parsing and handling in CoreRequest
|
||||
|
||||
- CoreRequest.parseUnixSocketUrl now strips http:// and https:// prefixes so it correctly parses both full URLs (e.g. http://unix:/path/to/socket:/route) and already-stripped unix: paths.
|
||||
- Node.js CoreRequest now passes the original request URL to parseUnixSocketUrl instead of options.path, preventing incorrect socketPath/path extraction.
|
||||
- Fixes connection failures when using unix socket URLs (for example when targeting Docker via http://unix:/var/run/docker.sock:/v1.24/...).
|
||||
|
||||
## 2025-11-16 - 4.4.0 - feat(core)
|
||||
Add Bun and Deno runtime support, unify core loader, unix-socket support and cross-runtime streaming/tests
|
||||
|
||||
- package.json: expose ./core_bun and ./core_deno in exports and add runtime-related keywords
|
||||
- Core dynamic loader (ts/core/index.ts): detect Bun and Deno at runtime and load corresponding implementations
|
||||
- New runtime modules: added ts/core_bun/* and ts/core_deno/* (response, types, index) to provide Bun and Deno CoreResponse/CoreRequest wrappers
|
||||
- Client streaming: SmartRequest no longer immediately deletes temporary __nodeStream and __rawStreamFunc props — CoreRequest implementations handle them; temporary properties are cleaned up after CoreRequest is created
|
||||
- Node.js request: core_node/request.ts converts web ReadableStream to Node.js Readable via stream.Readable.fromWeb and pipes it; also supports passing requestDataFunc for raw streaming
|
||||
- core_node/plugins: export stream helper and rework third-party exports (agentkeepalive, form-data) for Node implementation
|
||||
- CoreResponse for Bun/Deno: new implementations wrap native fetch Response and expose raw(), stream(), and streamNode() behavior (streamNode() throws in Bun/Deno with guidance to use web streams)
|
||||
- Tests: added unified cross-runtime streaming tests and separate unix-socket tests for Node/Bun/Deno with Docker-socket availability checks; removed old node-only streaming test
|
||||
- Docs/readme: updated to describe Node, Bun, Deno, and browser support, unix socket behavior per runtime, and new test conventions
|
||||
|
||||
## 2025-11-16 - 4.3.8 - fix(core)
|
||||
Ensure correct ArrayBuffer return, fix fetch body typing, reorganize node-only tests, and bump tsbuild devDependency
|
||||
|
||||
|
||||
359
deno.lock
generated
359
deno.lock
generated
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"version": "5",
|
||||
"specifiers": {
|
||||
"npm:@git.zone/tsbuild@^2.7.1": "2.7.1",
|
||||
"npm:@git.zone/tsrun@^1.6.2": "1.6.2",
|
||||
"npm:@git.zone/tstest@^2.7.0": "2.7.0",
|
||||
"npm:@git.zone/tsbuild@^3.1.0": "3.1.0",
|
||||
"npm:@git.zone/tsrun@2": "2.0.0",
|
||||
"npm:@git.zone/tstest@^2.8.1": "2.8.1",
|
||||
"npm:@push.rocks/smartenv@6": "6.0.0",
|
||||
"npm:@push.rocks/smartpath@6": "6.0.0",
|
||||
"npm:@push.rocks/smartpromise@^4.0.4": "4.2.3",
|
||||
"npm:@push.rocks/smarturl@^3.1.0": "3.1.0",
|
||||
"npm:@types/node@^22.9.0": "22.18.13",
|
||||
"npm:agentkeepalive@^4.5.0": "4.6.0",
|
||||
"npm:form-data@^4.0.4": "4.0.4"
|
||||
"npm:form-data@^4.0.5": "4.0.5"
|
||||
},
|
||||
"npm": {
|
||||
"@api.global/typedrequest-interfaces@2.0.2": {
|
||||
@@ -61,7 +61,7 @@
|
||||
"@push.rocks/smartopen",
|
||||
"@push.rocks/smartpath@6.0.0",
|
||||
"@push.rocks/smartpromise",
|
||||
"@push.rocks/smartrequest@4.3.4",
|
||||
"@push.rocks/smartrequest@4.4.2",
|
||||
"@push.rocks/smartrx",
|
||||
"@push.rocks/smartsitemap",
|
||||
"@push.rocks/smartstream",
|
||||
@@ -161,8 +161,8 @@
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-crypto/util/-/util-5.2.0.tgz"
|
||||
},
|
||||
"@aws-sdk/client-s3@3.931.0": {
|
||||
"integrity": "sha512-p+ZSRvmylk/pNImGDvLt3lOkILOexNcYvsCjvN2TR9X8RvxvPURISVp2qdGKdwUr/zkshteg1x/30GYlcTKs5g==",
|
||||
"@aws-sdk/client-s3@3.932.0": {
|
||||
"integrity": "sha512-qrlbJ3W5QR3Gzz2S+yaItH8ZhX7vaeA4j4fDAi8+0FmsVhXOfBbomWr+JO1wk/YojZMdyLfmfYRHrJvAQsLFVw==",
|
||||
"dependencies": [
|
||||
"@aws-crypto/sha1-browser",
|
||||
"@aws-crypto/sha256-browser",
|
||||
@@ -220,10 +220,10 @@
|
||||
"@smithy/util-waiter",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/client-s3/-/client-s3-3.931.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/client-s3/-/client-s3-3.932.0.tgz"
|
||||
},
|
||||
"@aws-sdk/client-sso@3.931.0": {
|
||||
"integrity": "sha512-GM/CARsIUQGEspM9VhZaftFVXnNtFNUUXjpM1ePO4CHk1J/VFvXcsQr3SHWIs0F4Ll6pvy5LpcRlWW5pK7T4aQ==",
|
||||
"@aws-sdk/client-sso@3.932.0": {
|
||||
"integrity": "sha512-XHqHa5iv2OQsKoM2tUQXs7EAyryploC00Wg0XSFra/KAKqyGizUb5XxXsGlyqhebB29Wqur+zwiRwNmejmN0+Q==",
|
||||
"dependencies": [
|
||||
"@aws-crypto/sha256-browser",
|
||||
"@aws-crypto/sha256-js",
|
||||
@@ -264,10 +264,10 @@
|
||||
"@smithy/util-utf8@4.2.0",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/client-sso/-/client-sso-3.931.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/client-sso/-/client-sso-3.932.0.tgz"
|
||||
},
|
||||
"@aws-sdk/core@3.931.0": {
|
||||
"integrity": "sha512-l/b6AQbto4TuXL2FIm7Z+tbVjrp0LN7ESm97Sf3nneB0vjKtB6R0TS/IySzCYMgyOC3Hxz+Ka34HJXZk9eXTFw==",
|
||||
"@aws-sdk/core@3.932.0": {
|
||||
"integrity": "sha512-AS8gypYQCbNojwgjvZGkJocC2CoEICDx9ZJ15ILsv+MlcCVLtUJSRSx3VzJOUY2EEIaGLRrPNlIqyn/9/fySvA==",
|
||||
"dependencies": [
|
||||
"@aws-sdk/types",
|
||||
"@aws-sdk/xml-builder",
|
||||
@@ -283,10 +283,10 @@
|
||||
"@smithy/util-utf8@4.2.0",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/core/-/core-3.931.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/core/-/core-3.932.0.tgz"
|
||||
},
|
||||
"@aws-sdk/credential-provider-env@3.931.0": {
|
||||
"integrity": "sha512-dTNBpkKXyBdcpEjyfgkE/EFU/0NRoukLs+Pj0S8K1Dg216J9uIijpi6CaBBN+HvnaTlEItm2tzXiJpPVI+TqHQ==",
|
||||
"@aws-sdk/credential-provider-env@3.932.0": {
|
||||
"integrity": "sha512-ozge/c7NdHUDyHqro6+P5oHt8wfKSUBN+olttiVfBe9Mw3wBMpPa3gQ0pZnG+gwBkKskBuip2bMR16tqYvUSEA==",
|
||||
"dependencies": [
|
||||
"@aws-sdk/core",
|
||||
"@aws-sdk/types",
|
||||
@@ -294,10 +294,10 @@
|
||||
"@smithy/types",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/credential-provider-env/-/credential-provider-env-3.931.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/credential-provider-env/-/credential-provider-env-3.932.0.tgz"
|
||||
},
|
||||
"@aws-sdk/credential-provider-http@3.931.0": {
|
||||
"integrity": "sha512-7Ge26fhMDn51BTbHgopx5+uOl4I47k15BDzYc4YT6zyjS99uycYNCA7zB500DGTTn2HK27ZDTyAyhTKZGxRxbA==",
|
||||
"@aws-sdk/credential-provider-http@3.932.0": {
|
||||
"integrity": "sha512-b6N9Nnlg8JInQwzBkUq5spNaXssM3h3zLxGzpPrnw0nHSIWPJPTbZzA5Ca285fcDUFuKP+qf3qkuqlAjGOdWhg==",
|
||||
"dependencies": [
|
||||
"@aws-sdk/core",
|
||||
"@aws-sdk/types",
|
||||
@@ -310,10 +310,10 @@
|
||||
"@smithy/util-stream",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/credential-provider-http/-/credential-provider-http-3.931.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/credential-provider-http/-/credential-provider-http-3.932.0.tgz"
|
||||
},
|
||||
"@aws-sdk/credential-provider-ini@3.931.0": {
|
||||
"integrity": "sha512-uzicpP7IHBxvAMjwGdmeke2bGTxjsKCSW7N48zuv0t0d56hmGHfcZIK5p4ry2OBJxzScp182OUAdAEG8wuSuuA==",
|
||||
"@aws-sdk/credential-provider-ini@3.932.0": {
|
||||
"integrity": "sha512-ZBjSAXVGy7danZRHCRMJQ7sBkG1Dz39thYlvTiUaf9BKZ+8ymeiFhuTeV1OkWUBBnY0ki2dVZJvboTqfINhNxA==",
|
||||
"dependencies": [
|
||||
"@aws-sdk/core",
|
||||
"@aws-sdk/credential-provider-env",
|
||||
@@ -329,10 +329,10 @@
|
||||
"@smithy/types",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.931.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.932.0.tgz"
|
||||
},
|
||||
"@aws-sdk/credential-provider-node@3.931.0": {
|
||||
"integrity": "sha512-eO8mfWNHz0dyYdVfPLVzmqXaSA3agZF/XvBO9/fRU90zCb8lKlXfgUmghGW7LhDkiv2v5uuizUiag7GsKoIcJw==",
|
||||
"@aws-sdk/credential-provider-node@3.932.0": {
|
||||
"integrity": "sha512-SEG9t2taBT86qe3gTunfrK8BxT710GVLGepvHr+X5Pw+qW225iNRaGN0zJH+ZE/j91tcW9wOaIoWnURkhR5wIg==",
|
||||
"dependencies": [
|
||||
"@aws-sdk/credential-provider-env",
|
||||
"@aws-sdk/credential-provider-http",
|
||||
@@ -347,10 +347,10 @@
|
||||
"@smithy/types",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/credential-provider-node/-/credential-provider-node-3.931.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/credential-provider-node/-/credential-provider-node-3.932.0.tgz"
|
||||
},
|
||||
"@aws-sdk/credential-provider-process@3.931.0": {
|
||||
"integrity": "sha512-8Mu9r+5BUKqmKSI/WYHl5o4GeoonEb51RmoLEqG6431Uz4Y8C6gzAT69yjOJ+MwoWQ2Os37OZLOTv7SgxyOgrQ==",
|
||||
"@aws-sdk/credential-provider-process@3.932.0": {
|
||||
"integrity": "sha512-BodZYKvT4p/Dkm28Ql/FhDdS1+p51bcZeMMu2TRtU8PoMDHnVDhHz27zASEKSZwmhvquxHrZHB0IGuVqjZUtSQ==",
|
||||
"dependencies": [
|
||||
"@aws-sdk/core",
|
||||
"@aws-sdk/types",
|
||||
@@ -359,10 +359,10 @@
|
||||
"@smithy/types",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/credential-provider-process/-/credential-provider-process-3.931.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/credential-provider-process/-/credential-provider-process-3.932.0.tgz"
|
||||
},
|
||||
"@aws-sdk/credential-provider-sso@3.931.0": {
|
||||
"integrity": "sha512-FP31lfMgNMDG4ZDX4NUZ+uoHWn76etcG8UWEgzZb4YOPV4M8a7gwU95iD+RBaK4lV3KvwH2tu68Hmne1qQpFqQ==",
|
||||
"@aws-sdk/credential-provider-sso@3.932.0": {
|
||||
"integrity": "sha512-XYmkv+ltBjjmPZ6AmR1ZQZkQfD0uzG61M18/Lif3HAGxyg3dmod0aWx9aL6lj9SvxAGqzscrx5j4PkgLqjZruw==",
|
||||
"dependencies": [
|
||||
"@aws-sdk/client-sso",
|
||||
"@aws-sdk/core",
|
||||
@@ -373,10 +373,10 @@
|
||||
"@smithy/types",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.931.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.932.0.tgz"
|
||||
},
|
||||
"@aws-sdk/credential-provider-web-identity@3.931.0": {
|
||||
"integrity": "sha512-hfX0Buw2+ie0FBiSFMmnXfugQc9fO0KvEojnNnzhk4utlWjZobMcUprOQ/VKUueg0Kga1b1xu8gEP6g1aEh3zw==",
|
||||
"@aws-sdk/credential-provider-web-identity@3.932.0": {
|
||||
"integrity": "sha512-Yw/hYNnC1KHuVIQF9PkLXbuKN7ljx70OSbJYDRufllQvej3kRwNcqQSnzI1M4KaObccqKaE6srg22DqpPy9p8w==",
|
||||
"dependencies": [
|
||||
"@aws-sdk/core",
|
||||
"@aws-sdk/nested-clients",
|
||||
@@ -386,7 +386,7 @@
|
||||
"@smithy/types",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.931.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.932.0.tgz"
|
||||
},
|
||||
"@aws-sdk/middleware-bucket-endpoint@3.930.0": {
|
||||
"integrity": "sha512-cnCLWeKPYgvV4yRYPFH6pWMdUByvu2cy2BAlfsPpvnm4RaVioztyvxmQj5PmVN5fvWs5w/2d6U7le8X9iye2sA==",
|
||||
@@ -411,8 +411,8 @@
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.930.0.tgz"
|
||||
},
|
||||
"@aws-sdk/middleware-flexible-checksums@3.931.0": {
|
||||
"integrity": "sha512-eYWwUKeEommCrrm0Ro6fGDwVO0x2bL3niOmSnHIlIdpu7ruzAGaphj+2MekCxaSPORzkZ3yheHUzV45D8Qj63A==",
|
||||
"@aws-sdk/middleware-flexible-checksums@3.932.0": {
|
||||
"integrity": "sha512-hyvRz/XS/0HTHp9/Ld1mKwpOi7bZu5olI42+T112rkCTbt1bewkygzEl4oflY4H7cKMamQusYoL0yBUD/QSEvA==",
|
||||
"dependencies": [
|
||||
"@aws-crypto/crc32",
|
||||
"@aws-crypto/crc32c",
|
||||
@@ -428,7 +428,7 @@
|
||||
"@smithy/util-utf8@4.2.0",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.931.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.932.0.tgz"
|
||||
},
|
||||
"@aws-sdk/middleware-host-header@3.930.0": {
|
||||
"integrity": "sha512-x30jmm3TLu7b/b+67nMyoV0NlbnCVT5DI57yDrhXAPCtdgM1KtdLWt45UcHpKOm1JsaIkmYRh2WYu7Anx4MG0g==",
|
||||
@@ -469,8 +469,8 @@
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.930.0.tgz"
|
||||
},
|
||||
"@aws-sdk/middleware-sdk-s3@3.931.0": {
|
||||
"integrity": "sha512-uWF78ht8Wgxljn6y0cEcIWfbeTVnJ0cE1Gha9ScCqscmuBCpHuFMSd/p53w3whoDhpQL3ln9mOyY3tfST/NUQA==",
|
||||
"@aws-sdk/middleware-sdk-s3@3.932.0": {
|
||||
"integrity": "sha512-bYMHxqQzseaAP9Z5qLI918z5AtbAnZRRtFi3POb4FLZyreBMgCgBNaPkIhdgywnkqaydTWvbMBX4s9f4gUwlTw==",
|
||||
"dependencies": [
|
||||
"@aws-sdk/core",
|
||||
"@aws-sdk/types",
|
||||
@@ -487,7 +487,7 @@
|
||||
"@smithy/util-utf8@4.2.0",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.931.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.932.0.tgz"
|
||||
},
|
||||
"@aws-sdk/middleware-ssec@3.930.0": {
|
||||
"integrity": "sha512-N2/SvodmaDS6h7CWfuapt3oJyn1T2CBz0CsDIiTDv9cSagXAVFjPdm2g4PFJqrNBeqdDIoYBnnta336HmamWHg==",
|
||||
@@ -498,8 +498,8 @@
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/middleware-ssec/-/middleware-ssec-3.930.0.tgz"
|
||||
},
|
||||
"@aws-sdk/middleware-user-agent@3.931.0": {
|
||||
"integrity": "sha512-Ftd+f3+y5KNYKzLXaGknwJ9hCkFWshi5C9TLLsz+fEohWc1FvIKU7MlXTeFms2eN76TTVHuG8N2otaujl6CuHg==",
|
||||
"@aws-sdk/middleware-user-agent@3.932.0": {
|
||||
"integrity": "sha512-9BGTbJyA/4PTdwQWE9hAFIJGpsYkyEW20WON3i15aDqo5oRZwZmqaVageOD57YYqG8JDJjvcwKyDdR4cc38dvg==",
|
||||
"dependencies": [
|
||||
"@aws-sdk/core",
|
||||
"@aws-sdk/types",
|
||||
@@ -509,10 +509,10 @@
|
||||
"@smithy/types",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.931.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.932.0.tgz"
|
||||
},
|
||||
"@aws-sdk/nested-clients@3.931.0": {
|
||||
"integrity": "sha512-6/dXrX2nWgiWdHxooEtmKpOErms4+79AQawEvhhxpLPpa+tixl4i/MSFgHk9sjkGv5a1/P3DbnedpZWl+2wMOg==",
|
||||
"@aws-sdk/nested-clients@3.932.0": {
|
||||
"integrity": "sha512-E2ucBfiXSpxZflHTf3UFbVwao4+7v7ctAeg8SWuglc1UMqMlpwMFFgWiSONtsf0SR3+ZDoWGATyCXOfDWerJuw==",
|
||||
"dependencies": [
|
||||
"@aws-crypto/sha256-browser",
|
||||
"@aws-crypto/sha256-js",
|
||||
@@ -553,7 +553,7 @@
|
||||
"@smithy/util-utf8@4.2.0",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/nested-clients/-/nested-clients-3.931.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/nested-clients/-/nested-clients-3.932.0.tgz"
|
||||
},
|
||||
"@aws-sdk/region-config-resolver@3.930.0": {
|
||||
"integrity": "sha512-KL2JZqH6aYeQssu1g1KuWsReupdfOoxD6f1as2VC+rdwYFUu4LfzMsFfXnBvvQWWqQ7rZHWOw1T+o5gJmg7Dzw==",
|
||||
@@ -566,8 +566,8 @@
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/region-config-resolver/-/region-config-resolver-3.930.0.tgz"
|
||||
},
|
||||
"@aws-sdk/signature-v4-multi-region@3.931.0": {
|
||||
"integrity": "sha512-EGYYDSSk7k1xbSHtb8MfEMILf5achdNnnsYKgFk0+Oul3tPQ4xUmOt5qRP6sOO3/LQHF37gBYHUF9OSA/+uVCw==",
|
||||
"@aws-sdk/signature-v4-multi-region@3.932.0": {
|
||||
"integrity": "sha512-NCIRJvoRc9246RZHIusY1+n/neeG2yGhBGdKhghmrNdM+mLLN6Ii7CKFZjx3DhxtpHMpl1HWLTMhdVrGwP2upw==",
|
||||
"dependencies": [
|
||||
"@aws-sdk/middleware-sdk-s3",
|
||||
"@aws-sdk/types",
|
||||
@@ -576,10 +576,10 @@
|
||||
"@smithy/types",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.931.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.932.0.tgz"
|
||||
},
|
||||
"@aws-sdk/token-providers@3.931.0": {
|
||||
"integrity": "sha512-dr+02X9oxqmXG0856odFJ7wAXy12pr/tq2Zg+IS0TDThFvgtvx4yChkpqmc89wGoW+Aly47JPfPUXh0IMpGzIg==",
|
||||
"@aws-sdk/token-providers@3.932.0": {
|
||||
"integrity": "sha512-43u82ulVuHK4zWhcSPyuPS18l0LNHi3QJQ1YtP2MfP8bPf5a6hMYp5e3lUr9oTDEWcpwBYtOW0m1DVmoU/3veA==",
|
||||
"dependencies": [
|
||||
"@aws-sdk/core",
|
||||
"@aws-sdk/nested-clients",
|
||||
@@ -589,7 +589,7 @@
|
||||
"@smithy/types",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/token-providers/-/token-providers-3.931.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/token-providers/-/token-providers-3.932.0.tgz"
|
||||
},
|
||||
"@aws-sdk/types@3.930.0": {
|
||||
"integrity": "sha512-we/vaAgwlEFW7IeftmCLlLMw+6hFs3DzZPJw7lVHbj/5HJ0bz9gndxEsS2lQoeJ1zhiiLqAqvXxmM43s0MBg0A==",
|
||||
@@ -634,8 +634,8 @@
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.930.0.tgz"
|
||||
},
|
||||
"@aws-sdk/util-user-agent-node@3.931.0": {
|
||||
"integrity": "sha512-j5if01rt7JCGYDVXck39V7IUyKAN73vKUPzmu+jp1apU3Q0lLSTZA/HCfL2HkMUKVLE67ibjKb+NCoEg0QhujA==",
|
||||
"@aws-sdk/util-user-agent-node@3.932.0": {
|
||||
"integrity": "sha512-/kC6cscHrZL74TrZtgiIL5jJNbVsw9duGGPurmaVgoCbP7NnxyaSWEurbNV3VPNPhNE3bV3g4Ci+odq+AlsYQg==",
|
||||
"dependencies": [
|
||||
"@aws-sdk/middleware-user-agent",
|
||||
"@aws-sdk/types",
|
||||
@@ -643,7 +643,7 @@
|
||||
"@smithy/types",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.931.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.932.0.tgz"
|
||||
},
|
||||
"@aws-sdk/xml-builder@3.930.0": {
|
||||
"integrity": "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA==",
|
||||
@@ -679,9 +679,9 @@
|
||||
"integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==",
|
||||
"tarball": "https://verdaccio.lossless.digital/@borewit/text-codec/-/text-codec-0.1.1.tgz"
|
||||
},
|
||||
"@cloudflare/workers-types@4.20251113.0": {
|
||||
"integrity": "sha512-AMntYysoQzuc1aXLjkNLmFcuyxzCrZ/dfatnt2Lw4rf3aFtnM6RZKZ+zLhb+xjgjlrI7tzC86ay3gqYqKa2Qxg==",
|
||||
"tarball": "https://verdaccio.lossless.digital/@cloudflare/workers-types/-/workers-types-4.20251113.0.tgz"
|
||||
"@cloudflare/workers-types@4.20251117.0": {
|
||||
"integrity": "sha512-4U6phQE/qbmWuJX/0cTk3iOx9KyhWmo34okKb2BSS09UMRWLCwML+P3dnHxCHnSQij8c0orvdzMr9hyqgJGNgw==",
|
||||
"tarball": "https://verdaccio.lossless.digital/@cloudflare/workers-types/-/workers-types-4.20251117.0.tgz"
|
||||
},
|
||||
"@colors/colors@1.6.0": {
|
||||
"integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
|
||||
@@ -713,8 +713,8 @@
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@design.estate/dees-comms/-/dees-comms-1.0.27.tgz"
|
||||
},
|
||||
"@design.estate/dees-domtools@2.3.3": {
|
||||
"integrity": "sha512-diIRuEWNRko508+eXDGVD9yxte+50VSuSsxBvWXUnE7ZPOLo9Y0oNyVi+R1Rb1AVJiXcGCORLdCtmCIcId40VA==",
|
||||
"@design.estate/dees-domtools@2.3.6": {
|
||||
"integrity": "sha512-cKaPNtSpp/ZuuXVx2dXO3K2FU3/HjC4ZkqtXb8Kl6yy9rNDbgtjcI4PuOk9Ux1SJzw7FgcxqVh7OSEV60htbmg==",
|
||||
"dependencies": [
|
||||
"@api.global/typedrequest",
|
||||
"@design.estate/dees-comms",
|
||||
@@ -735,17 +735,17 @@
|
||||
"lit",
|
||||
"sweet-scroll"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@design.estate/dees-domtools/-/dees-domtools-2.3.3.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@design.estate/dees-domtools/-/dees-domtools-2.3.6.tgz"
|
||||
},
|
||||
"@design.estate/dees-element@2.1.2": {
|
||||
"integrity": "sha512-ZiwvE411RJPHaYio26asQLnSmtJ6G1HRLYWbxW/HvCMbFtrcrXysP1y4PQ9KjdNfiQ4yoWPjTtwYMJjLE0NcbA==",
|
||||
"@design.estate/dees-element@2.1.3": {
|
||||
"integrity": "sha512-TjXWxVcdSPaT1IOk31ckfxvAZnJLuTxhFGsNCKoh63/UE2FVf6slp8//UFvN+ADigiA9ZsY0azkY99XbJCwDDA==",
|
||||
"dependencies": [
|
||||
"@design.estate/dees-domtools",
|
||||
"@push.rocks/isounique",
|
||||
"@push.rocks/smartrx",
|
||||
"lit"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@design.estate/dees-element/-/dees-element-2.1.2.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@design.estate/dees-element/-/dees-element-2.1.3.tgz"
|
||||
},
|
||||
"@emnapi/core@1.7.1": {
|
||||
"integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==",
|
||||
@@ -925,8 +925,8 @@
|
||||
"cpu": ["x64"],
|
||||
"tarball": "https://verdaccio.lossless.digital/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz"
|
||||
},
|
||||
"@git.zone/tsbuild@2.7.1": {
|
||||
"integrity": "sha512-O8TTc+LBp8hYy5+zA6AdoqdQQtVXTAd1L0gS/Ihz+QXgXvMdQKVINwpDFu6LS5NdVrGXzxnB63NQpfg5COIPnQ==",
|
||||
"@git.zone/tsbuild@3.1.0": {
|
||||
"integrity": "sha512-j8lMd84pmzWiU6NG3e+pyu0o41oo6mQVfcZv8kDsCrQwZMhoQV9Jp87MlU0i/XI5IZkqDjelG8Kx1QhOmbK+iQ==",
|
||||
"dependencies": [
|
||||
"@git.zone/tspublish",
|
||||
"@push.rocks/early",
|
||||
@@ -939,10 +939,10 @@
|
||||
"typescript@5.9.3"
|
||||
],
|
||||
"bin": true,
|
||||
"tarball": "https://verdaccio.lossless.digital/@git.zone/tsbuild/-/tsbuild-2.7.1.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@git.zone/tsbuild/-/tsbuild-3.1.0.tgz"
|
||||
},
|
||||
"@git.zone/tsbundle@2.5.1": {
|
||||
"integrity": "sha512-gBskgM3ECy9FEmhCWnQahDyFCAjjw/7emjx/KYM/FOlPqGV+hmYzt368zwSlkzOGgYF8k9OZ+mp6vexDL/+f2w==",
|
||||
"@git.zone/tsbundle@2.5.2": {
|
||||
"integrity": "sha512-EYTCfunqoxhxkowREZ+cJnww6eDh9cL18HJbHbSZ+vxzNeyS9x8mT9aqRlWkI7zgpvgDlGIYlyRUlUISXkQO6Q==",
|
||||
"dependencies": [
|
||||
"@push.rocks/early",
|
||||
"@push.rocks/smartcli",
|
||||
@@ -961,7 +961,7 @@
|
||||
"typescript@5.8.3"
|
||||
],
|
||||
"bin": true,
|
||||
"tarball": "https://verdaccio.lossless.digital/@git.zone/tsbundle/-/tsbundle-2.5.1.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@git.zone/tsbundle/-/tsbundle-2.5.2.tgz"
|
||||
},
|
||||
"@git.zone/tspublish@1.10.3": {
|
||||
"integrity": "sha512-o2/jvNsdLC8SRdH1kQ7JjNOQNu9el0FpJ/QOW3mgiC5C9reuTp18iU4kijsVVLgvw4KZv6Z289SoKPh3HPsS0g==",
|
||||
@@ -973,7 +973,7 @@
|
||||
"@push.rocks/smartlog",
|
||||
"@push.rocks/smartnpm",
|
||||
"@push.rocks/smartpath@6.0.0",
|
||||
"@push.rocks/smartrequest@4.3.4",
|
||||
"@push.rocks/smartrequest@4.4.2",
|
||||
"@push.rocks/smartshell"
|
||||
],
|
||||
"bin": true,
|
||||
@@ -989,12 +989,22 @@
|
||||
"bin": true,
|
||||
"tarball": "https://verdaccio.lossless.digital/@git.zone/tsrun/-/tsrun-1.6.2.tgz"
|
||||
},
|
||||
"@git.zone/tstest@2.7.0": {
|
||||
"integrity": "sha512-ET532UpIAE86Ifw4P1Y2U7AJalKxM/Q88+kKN854dXSX+s5GgnRy0ysrPcSCUkJfENEvou1b0er1xcotUKx6hw==",
|
||||
"@git.zone/tsrun@2.0.0": {
|
||||
"integrity": "sha512-yA6zCjL+kn7xfZe6sL/m4K+zYqgkznG/pF6++i/E17iwzpG6dHmW+VZmYldHe86sW4DcLMvqM6CxM+KlgaEpKw==",
|
||||
"dependencies": [
|
||||
"@push.rocks/smartfile",
|
||||
"@push.rocks/smartshell",
|
||||
"tsx"
|
||||
],
|
||||
"bin": true,
|
||||
"tarball": "https://verdaccio.lossless.digital/@git.zone/tsrun/-/tsrun-2.0.0.tgz"
|
||||
},
|
||||
"@git.zone/tstest@2.8.1": {
|
||||
"integrity": "sha512-0Sct9XsbrmAQgKNoW/jBNPMLllKVI+W6/aVkj9DEguiEnysmxLb3xRyoay06lxTGSBe5dA5uNULrdycdQ9slgQ==",
|
||||
"dependencies": [
|
||||
"@api.global/typedserver",
|
||||
"@git.zone/tsbundle",
|
||||
"@git.zone/tsrun",
|
||||
"@git.zone/tsrun@1.6.2",
|
||||
"@push.rocks/consolecolor",
|
||||
"@push.rocks/qenv",
|
||||
"@push.rocks/smartbrowser",
|
||||
@@ -1010,7 +1020,7 @@
|
||||
"@push.rocks/smartnetwork",
|
||||
"@push.rocks/smartpath@6.0.0",
|
||||
"@push.rocks/smartpromise",
|
||||
"@push.rocks/smartrequest@4.3.4",
|
||||
"@push.rocks/smartrequest@4.4.2",
|
||||
"@push.rocks/smarts3",
|
||||
"@push.rocks/smartshell",
|
||||
"@push.rocks/smarttime",
|
||||
@@ -1019,7 +1029,7 @@
|
||||
"ws@8.18.3"
|
||||
],
|
||||
"bin": true,
|
||||
"tarball": "https://verdaccio.lossless.digital/@git.zone/tstest/-/tstest-2.7.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@git.zone/tstest/-/tstest-2.8.1.tgz"
|
||||
},
|
||||
"@happy-dom/global-registrator@15.11.7": {
|
||||
"integrity": "sha512-mfOoUlIw8VBiJYPrl5RZfMzkXC/z7gbSpi2ecycrj/gRWLq2CMV+Q+0G+JPjeOmuNFgg0skEIzkVFzVYFP6URw==",
|
||||
@@ -1184,8 +1194,8 @@
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@pdf-lib/upng/-/upng-1.0.1.tgz"
|
||||
},
|
||||
"@peculiar/asn1-cms@2.5.0": {
|
||||
"integrity": "sha512-p0SjJ3TuuleIvjPM4aYfvYw8Fk1Hn/zAVyPJZTtZ2eE9/MIer6/18ROxX6N/e6edVSfvuZBqhxAj3YgsmSjQ/A==",
|
||||
"@peculiar/asn1-cms@2.6.0": {
|
||||
"integrity": "sha512-2uZqP+ggSncESeUF/9Su8rWqGclEfEiz1SyU02WX5fUONFfkjzS2Z/F1Li0ofSmf4JqYXIOdCAZqIXAIBAT1OA==",
|
||||
"dependencies": [
|
||||
"@peculiar/asn1-schema",
|
||||
"@peculiar/asn1-x509",
|
||||
@@ -1193,30 +1203,30 @@
|
||||
"asn1js",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-cms/-/asn1-cms-2.5.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-cms/-/asn1-cms-2.6.0.tgz"
|
||||
},
|
||||
"@peculiar/asn1-csr@2.5.0": {
|
||||
"integrity": "sha512-ioigvA6WSYN9h/YssMmmoIwgl3RvZlAYx4A/9jD2qaqXZwGcNlAxaw54eSx2QG1Yu7YyBC5Rku3nNoHrQ16YsQ==",
|
||||
"@peculiar/asn1-csr@2.6.0": {
|
||||
"integrity": "sha512-BeWIu5VpTIhfRysfEp73SGbwjjoLL/JWXhJ/9mo4vXnz3tRGm+NGm3KNcRzQ9VMVqwYS2RHlolz21svzRXIHPQ==",
|
||||
"dependencies": [
|
||||
"@peculiar/asn1-schema",
|
||||
"@peculiar/asn1-x509",
|
||||
"asn1js",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-csr/-/asn1-csr-2.5.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-csr/-/asn1-csr-2.6.0.tgz"
|
||||
},
|
||||
"@peculiar/asn1-ecc@2.5.0": {
|
||||
"integrity": "sha512-t4eYGNhXtLRxaP50h3sfO6aJebUCDGQACoeexcelL4roMFRRVgB20yBIu2LxsPh/tdW9I282gNgMOyg3ywg/mg==",
|
||||
"@peculiar/asn1-ecc@2.6.0": {
|
||||
"integrity": "sha512-FF3LMGq6SfAOwUG2sKpPXblibn6XnEIKa+SryvUl5Pik+WR9rmRA3OCiwz8R3lVXnYnyRkSZsSLdml8H3UiOcw==",
|
||||
"dependencies": [
|
||||
"@peculiar/asn1-schema",
|
||||
"@peculiar/asn1-x509",
|
||||
"asn1js",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-ecc/-/asn1-ecc-2.5.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-ecc/-/asn1-ecc-2.6.0.tgz"
|
||||
},
|
||||
"@peculiar/asn1-pfx@2.5.0": {
|
||||
"integrity": "sha512-Vj0d0wxJZA+Ztqfb7W+/iu8Uasw6hhKtCdLKXLG/P3kEPIQpqGI4P4YXlROfl7gOCqFIbgsj1HzFIFwQ5s20ug==",
|
||||
"@peculiar/asn1-pfx@2.6.0": {
|
||||
"integrity": "sha512-rtUvtf+tyKGgokHHmZzeUojRZJYPxoD/jaN1+VAB4kKR7tXrnDCA/RAWXAIhMJJC+7W27IIRGe9djvxKgsldCQ==",
|
||||
"dependencies": [
|
||||
"@peculiar/asn1-cms",
|
||||
"@peculiar/asn1-pkcs8",
|
||||
@@ -1225,20 +1235,20 @@
|
||||
"asn1js",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-pfx/-/asn1-pfx-2.5.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-pfx/-/asn1-pfx-2.6.0.tgz"
|
||||
},
|
||||
"@peculiar/asn1-pkcs8@2.5.0": {
|
||||
"integrity": "sha512-L7599HTI2SLlitlpEP8oAPaJgYssByI4eCwQq2C9eC90otFpm8MRn66PpbKviweAlhinWQ3ZjDD2KIVtx7PaVw==",
|
||||
"@peculiar/asn1-pkcs8@2.6.0": {
|
||||
"integrity": "sha512-KyQ4D8G/NrS7Fw3XCJrngxmjwO/3htnA0lL9gDICvEQ+GJ+EPFqldcJQTwPIdvx98Tua+WjkdKHSC0/Km7T+lA==",
|
||||
"dependencies": [
|
||||
"@peculiar/asn1-schema",
|
||||
"@peculiar/asn1-x509",
|
||||
"asn1js",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.5.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.6.0.tgz"
|
||||
},
|
||||
"@peculiar/asn1-pkcs9@2.5.0": {
|
||||
"integrity": "sha512-UgqSMBLNLR5TzEZ5ZzxR45Nk6VJrammxd60WMSkofyNzd3DQLSNycGWSK5Xg3UTYbXcDFyG8pA/7/y/ztVCa6A==",
|
||||
"@peculiar/asn1-pkcs9@2.6.0": {
|
||||
"integrity": "sha512-b78OQ6OciW0aqZxdzliXGYHASeCvvw5caqidbpQRYW2mBtXIX2WhofNXTEe7NyxTb0P6J62kAAWLwn0HuMF1Fw==",
|
||||
"dependencies": [
|
||||
"@peculiar/asn1-cms",
|
||||
"@peculiar/asn1-pfx",
|
||||
@@ -1249,49 +1259,49 @@
|
||||
"asn1js",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.5.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.6.0.tgz"
|
||||
},
|
||||
"@peculiar/asn1-rsa@2.5.0": {
|
||||
"integrity": "sha512-qMZ/vweiTHy9syrkkqWFvbT3eLoedvamcUdnnvwyyUNv5FgFXA3KP8td+ATibnlZ0EANW5PYRm8E6MJzEB/72Q==",
|
||||
"@peculiar/asn1-rsa@2.6.0": {
|
||||
"integrity": "sha512-Nu4C19tsrTsCp9fDrH+sdcOKoVfdfoQQ7S3VqjJU6vedR7tY3RLkQ5oguOIB3zFW33USDUuYZnPEQYySlgha4w==",
|
||||
"dependencies": [
|
||||
"@peculiar/asn1-schema",
|
||||
"@peculiar/asn1-x509",
|
||||
"asn1js",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-rsa/-/asn1-rsa-2.5.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-rsa/-/asn1-rsa-2.6.0.tgz"
|
||||
},
|
||||
"@peculiar/asn1-schema@2.5.0": {
|
||||
"integrity": "sha512-YM/nFfskFJSlHqv59ed6dZlLZqtZQwjRVJ4bBAiWV08Oc+1rSd5lDZcBEx0lGDHfSoH3UziI2pXt2UM33KerPQ==",
|
||||
"@peculiar/asn1-schema@2.6.0": {
|
||||
"integrity": "sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg==",
|
||||
"dependencies": [
|
||||
"asn1js",
|
||||
"pvtsutils",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-schema/-/asn1-schema-2.5.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-schema/-/asn1-schema-2.6.0.tgz"
|
||||
},
|
||||
"@peculiar/asn1-x509-attr@2.5.0": {
|
||||
"integrity": "sha512-9f0hPOxiJDoG/bfNLAFven+Bd4gwz/VzrCIIWc1025LEI4BXO0U5fOCTNDPbbp2ll+UzqKsZ3g61mpBp74gk9A==",
|
||||
"@peculiar/asn1-x509-attr@2.6.0": {
|
||||
"integrity": "sha512-MuIAXFX3/dc8gmoZBkwJWxUWOSvG4MMDntXhrOZpJVMkYX+MYc/rUAU2uJOved9iJEoiUx7//3D8oG83a78UJA==",
|
||||
"dependencies": [
|
||||
"@peculiar/asn1-schema",
|
||||
"@peculiar/asn1-x509",
|
||||
"asn1js",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.5.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.6.0.tgz"
|
||||
},
|
||||
"@peculiar/asn1-x509@2.5.0": {
|
||||
"integrity": "sha512-CpwtMCTJvfvYTFMuiME5IH+8qmDe3yEWzKHe7OOADbGfq7ohxeLaXwQo0q4du3qs0AII3UbLCvb9NF/6q0oTKQ==",
|
||||
"@peculiar/asn1-x509@2.6.0": {
|
||||
"integrity": "sha512-uzYbPEpoQiBoTq0/+jZtpM6Gq6zADBx+JNFP3yqRgziWBxQ/Dt/HcuvRfm9zJTPdRcBqPNdaRHTVwpyiq6iNMA==",
|
||||
"dependencies": [
|
||||
"@peculiar/asn1-schema",
|
||||
"asn1js",
|
||||
"pvtsutils",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-x509/-/asn1-x509-2.5.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/asn1-x509/-/asn1-x509-2.6.0.tgz"
|
||||
},
|
||||
"@peculiar/x509@1.14.0": {
|
||||
"integrity": "sha512-Yc4PDxN3OrxUPiXgU63c+ZRXKGE8YKF2McTciYhUHFtHVB0KMnjeFSU0qpztGhsp4P0uKix4+J2xEpIEDu8oXg==",
|
||||
"@peculiar/x509@1.14.2": {
|
||||
"integrity": "sha512-r2w1Hg6pODDs0zfAKHkSS5HLkOLSeburtcgwvlLLWWCixw+MmW3U6kD5ddyvc2Y2YdbGuVwCF2S2ASoU1cFAag==",
|
||||
"dependencies": [
|
||||
"@peculiar/asn1-cms",
|
||||
"@peculiar/asn1-csr",
|
||||
@@ -1305,7 +1315,7 @@
|
||||
"tslib@2.8.1",
|
||||
"tsyringe"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/x509/-/x509-1.14.0.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@peculiar/x509/-/x509-1.14.2.tgz"
|
||||
},
|
||||
"@pnpm/config.env-replace@1.1.0": {
|
||||
"integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==",
|
||||
@@ -1432,7 +1442,7 @@
|
||||
"@push.rocks/smartfile",
|
||||
"@push.rocks/smartpath@6.0.0",
|
||||
"@push.rocks/smartpromise",
|
||||
"@push.rocks/smartrequest@4.3.4",
|
||||
"@push.rocks/smartrequest@4.4.2",
|
||||
"@push.rocks/smartrx",
|
||||
"@push.rocks/smartstream",
|
||||
"@push.rocks/smartunique",
|
||||
@@ -1532,8 +1542,8 @@
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@push.rocks/smartcrypto/-/smartcrypto-2.0.4.tgz"
|
||||
},
|
||||
"@push.rocks/smartdata@5.16.4": {
|
||||
"integrity": "sha512-COiKw8yk9iAcLN44WmZHG8Gi0v+HGkgM8Osoq7Cns+UsOA+grPepqbN2r0XPG1fm5vOdJcaydi2ZU0xrnbGVvQ==",
|
||||
"@push.rocks/smartdata@5.16.7": {
|
||||
"integrity": "sha512-bu/YSIjQcwxWXkAsuhqE6zs7eT+bTIKV8+/H7TbbjpzeioLCyB3dZ/41cLZk37c/EYt4d4GHgZ0ww80OiKOUMg==",
|
||||
"dependencies": [
|
||||
"@push.rocks/lik",
|
||||
"@push.rocks/smartdelay",
|
||||
@@ -1548,7 +1558,7 @@
|
||||
"@tsclass/tsclass@9.3.0",
|
||||
"mongodb"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@push.rocks/smartdata/-/smartdata-5.16.4.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@push.rocks/smartdata/-/smartdata-5.16.7.tgz"
|
||||
},
|
||||
"@push.rocks/smartdelay@3.0.5": {
|
||||
"integrity": "sha512-mUuI7kj2f7ztjpic96FvRIlf2RsKBa5arw81AHNsndbxO6asRcxuWL8dTVxouEIK8YsBUlj0AsrCkHhMbLQdHw==",
|
||||
@@ -1638,7 +1648,7 @@
|
||||
"@push.rocks/smartmime",
|
||||
"@push.rocks/smartpath@6.0.0",
|
||||
"@push.rocks/smartpromise",
|
||||
"@push.rocks/smartrequest@4.3.4",
|
||||
"@push.rocks/smartrequest@4.4.2",
|
||||
"@push.rocks/smartstream",
|
||||
"@types/fs-extra",
|
||||
"@types/js-yaml@4.0.9",
|
||||
@@ -1753,8 +1763,8 @@
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@push.rocks/smartmime/-/smartmime-2.0.4.tgz"
|
||||
},
|
||||
"@push.rocks/smartmongo@2.0.12": {
|
||||
"integrity": "sha512-NglYiO14BikxnlvW6JF18FtopBtaWQEGAtPxHmmSCbyhU8Mi0aEFO7VgCasE9Kguba/wcR597qhcDEdcpBg1eQ==",
|
||||
"@push.rocks/smartmongo@2.0.14": {
|
||||
"integrity": "sha512-fvfPeCDDy5JaDBpVnsMKednsOoRzuYC0OszO8OoLOX/RaR755zIbRppCCK8Cez0hqd6eNkJGQeWdX/vXlJOPBA==",
|
||||
"dependencies": [
|
||||
"@push.rocks/mongodump",
|
||||
"@push.rocks/smartdata",
|
||||
@@ -1762,7 +1772,7 @@
|
||||
"@push.rocks/smartpromise",
|
||||
"mongodb-memory-server"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@push.rocks/smartmongo/-/smartmongo-2.0.12.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@push.rocks/smartmongo/-/smartmongo-2.0.14.tgz"
|
||||
},
|
||||
"@push.rocks/smartnetwork@4.4.0": {
|
||||
"integrity": "sha512-OvFtz41cvQ7lcXwaIOhghNUUlNoMxvwKDctbDvMyuZyEH08SpLjhyv2FuKbKL/mgwA/WxakTbohoC8SW7t+kiw==",
|
||||
@@ -1785,7 +1795,7 @@
|
||||
"@push.rocks/smartfile",
|
||||
"@push.rocks/smartpath@6.0.0",
|
||||
"@push.rocks/smartpromise",
|
||||
"@push.rocks/smartrequest@4.3.4",
|
||||
"@push.rocks/smartrequest@4.4.2",
|
||||
"@push.rocks/smarttime",
|
||||
"@push.rocks/smartversion",
|
||||
"package-json"
|
||||
@@ -1876,17 +1886,17 @@
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@push.rocks/smartrequest/-/smartrequest-2.1.0.tgz"
|
||||
},
|
||||
"@push.rocks/smartrequest@4.3.4": {
|
||||
"integrity": "sha512-b8UH4CS2QTccOjvmIJetOqK26eVz0CuCTkJdJDVU8j34KqPGpaWzn/4D3j3DCsTYRkAqr5nSjkzAFgCJj8eIiA==",
|
||||
"@push.rocks/smartrequest@4.4.2": {
|
||||
"integrity": "sha512-Om4y1Ce4YdSu8VoXREz2SgFz9pDxcFEm0+SC1YYa3RXd0AH2Mknaj/1XfvfMqojnK9L7N2z1fY4xX8tO1IwqFQ==",
|
||||
"dependencies": [
|
||||
"@push.rocks/smartenv@5.0.13",
|
||||
"@push.rocks/smartenv@6.0.0",
|
||||
"@push.rocks/smartpath@6.0.0",
|
||||
"@push.rocks/smartpromise",
|
||||
"@push.rocks/smarturl",
|
||||
"agentkeepalive",
|
||||
"form-data"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@push.rocks/smartrequest/-/smartrequest-4.3.4.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@push.rocks/smartrequest/-/smartrequest-4.4.2.tgz"
|
||||
},
|
||||
"@push.rocks/smartrouter@1.3.3": {
|
||||
"integrity": "sha512-1+xZEnWlhzqLWAaJ1zFNhQ0zgbfCWQl1DBT72LygLxTs+P0K8AwJKgqo/IX6CT55kGCFnPAZIYSbVJlGsgrB0w==",
|
||||
@@ -2048,7 +2058,7 @@
|
||||
"integrity": "sha512-tBcf+HaOIfeEsTMwgUZDtZERCxXQyRsWO8Ar5DjBdiSRchbhVGZQEBzXswMS0W5ZoRenjgPK+4tPW3JQGRTfbg==",
|
||||
"dependencies": [
|
||||
"@types/js-yaml@3.12.10",
|
||||
"js-yaml@3.14.1"
|
||||
"js-yaml@3.14.2"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@push.rocks/smartyaml/-/smartyaml-2.0.5.tgz"
|
||||
},
|
||||
@@ -2402,8 +2412,8 @@
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/config-resolver/-/config-resolver-4.4.3.tgz"
|
||||
},
|
||||
"@smithy/core@3.18.3": {
|
||||
"integrity": "sha512-qqpNskkbHOSfrbFbjhYj5o8VMXO26fvN1K/+HbCzUNlTuxgNcPRouUDNm+7D6CkN244WG7aK533Ne18UtJEgAA==",
|
||||
"@smithy/core@3.18.4": {
|
||||
"integrity": "sha512-o5tMqPZILBvvROfC8vC+dSVnWJl9a0u9ax1i1+Bq8515eYjUJqqk5XjjEsDLoeL5dSqGSh6WGdVx1eJ1E/Nwhw==",
|
||||
"dependencies": [
|
||||
"@smithy/middleware-serde",
|
||||
"@smithy/protocol-http",
|
||||
@@ -2416,7 +2426,7 @@
|
||||
"@smithy/uuid",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/core/-/core-3.18.3.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/core/-/core-3.18.4.tgz"
|
||||
},
|
||||
"@smithy/credential-provider-imds@4.2.5": {
|
||||
"integrity": "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ==",
|
||||
@@ -2554,8 +2564,8 @@
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/middleware-content-length/-/middleware-content-length-4.2.5.tgz"
|
||||
},
|
||||
"@smithy/middleware-endpoint@4.3.10": {
|
||||
"integrity": "sha512-SoAag3QnWBFoXjwa1jenEThkzJYClidZUyqsLKwWZ8kOlZBwehrLBp4ygVDjNEM2a2AamCQ2FBA/HuzKJ/LiTA==",
|
||||
"@smithy/middleware-endpoint@4.3.11": {
|
||||
"integrity": "sha512-eJXq9VJzEer1W7EQh3HY2PDJdEcEUnv6sKuNt4eVjyeNWcQFS4KmnY+CKkYOIR6tSqarn6bjjCqg1UB+8UJiPQ==",
|
||||
"dependencies": [
|
||||
"@smithy/core",
|
||||
"@smithy/middleware-serde",
|
||||
@@ -2566,10 +2576,10 @@
|
||||
"@smithy/util-middleware",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.10.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.11.tgz"
|
||||
},
|
||||
"@smithy/middleware-retry@4.4.10": {
|
||||
"integrity": "sha512-6fOwX34gXxcqKa3bsG0mR0arc2Cw4ddOS6tp3RgUD2yoTrDTbQ2aVADnDjhUuxaiDZN2iilxndgGDhnpL/XvJA==",
|
||||
"@smithy/middleware-retry@4.4.11": {
|
||||
"integrity": "sha512-EL5OQHvFOKneJVRgzRW4lU7yidSwp/vRJOe542bHgExN3KNThr1rlg0iE4k4SnA+ohC+qlUxoK+smKeAYPzfAQ==",
|
||||
"dependencies": [
|
||||
"@smithy/node-config-provider",
|
||||
"@smithy/protocol-http",
|
||||
@@ -2581,16 +2591,16 @@
|
||||
"@smithy/uuid",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/middleware-retry/-/middleware-retry-4.4.10.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/middleware-retry/-/middleware-retry-4.4.11.tgz"
|
||||
},
|
||||
"@smithy/middleware-serde@4.2.5": {
|
||||
"integrity": "sha512-La1ldWTJTZ5NqQyPqnCNeH9B+zjFhrNoQIL1jTh4zuqXRlmXhxYHhMtI1/92OlnoAtp6JoN7kzuwhWoXrBwPqg==",
|
||||
"@smithy/middleware-serde@4.2.6": {
|
||||
"integrity": "sha512-VkLoE/z7e2g8pirwisLz8XJWedUSY8my/qrp81VmAdyrhi94T+riBfwP+AOEEFR9rFTSonC/5D2eWNmFabHyGQ==",
|
||||
"dependencies": [
|
||||
"@smithy/protocol-http",
|
||||
"@smithy/types",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/middleware-serde/-/middleware-serde-4.2.5.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/middleware-serde/-/middleware-serde-4.2.6.tgz"
|
||||
},
|
||||
"@smithy/middleware-stack@4.2.5": {
|
||||
"integrity": "sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ==",
|
||||
@@ -2683,8 +2693,8 @@
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/signature-v4/-/signature-v4-5.3.5.tgz"
|
||||
},
|
||||
"@smithy/smithy-client@4.9.6": {
|
||||
"integrity": "sha512-hGz42hggqReicRRZUvrKDQiAmoJnx1Q+XfAJnYAGu544gOfxQCAC3hGGD7+Px2gEUUxB/kKtQV7LOtBRNyxteQ==",
|
||||
"@smithy/smithy-client@4.9.7": {
|
||||
"integrity": "sha512-pskaE4kg0P9xNQWihfqlTMyxyFR3CH6Sr6keHYghgyqqDXzjl2QJg5lAzuVe/LzZiOzcbcVtxKYi1/fZPt/3DA==",
|
||||
"dependencies": [
|
||||
"@smithy/core",
|
||||
"@smithy/middleware-endpoint",
|
||||
@@ -2694,7 +2704,7 @@
|
||||
"@smithy/util-stream",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/smithy-client/-/smithy-client-4.9.6.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/smithy-client/-/smithy-client-4.9.7.tgz"
|
||||
},
|
||||
"@smithy/types@4.9.0": {
|
||||
"integrity": "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==",
|
||||
@@ -2758,18 +2768,18 @@
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz"
|
||||
},
|
||||
"@smithy/util-defaults-mode-browser@4.3.9": {
|
||||
"integrity": "sha512-Bh5bU40BgdkXE2BcaNazhNtEXi1TC0S+1d84vUwv5srWfvbeRNUKFzwKQgC6p6MXPvEgw+9+HdX3pOwT6ut5aw==",
|
||||
"@smithy/util-defaults-mode-browser@4.3.10": {
|
||||
"integrity": "sha512-3iA3JVO1VLrP21FsZZpMCeF93aqP3uIOMvymAT3qHIJz2YlgDeRvNUspFwCNqd/j3qqILQJGtsVQnJZICh/9YA==",
|
||||
"dependencies": [
|
||||
"@smithy/property-provider",
|
||||
"@smithy/smithy-client",
|
||||
"@smithy/types",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.9.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.10.tgz"
|
||||
},
|
||||
"@smithy/util-defaults-mode-node@4.2.12": {
|
||||
"integrity": "sha512-EHZwe1E9Q7umImIyCKQg/Cm+S+7rjXxCRvfGmKifqwYvn7M8M4ZcowwUOQzvuuxUUmdzCkqL0Eq0z1m74Pq6pw==",
|
||||
"@smithy/util-defaults-mode-node@4.2.13": {
|
||||
"integrity": "sha512-PTc6IpnpSGASuzZAgyUtaVfOFpU0jBD2mcGwrgDuHf7PlFgt5TIPxCYBDbFQs06jxgeV3kd/d/sok1pzV0nJRg==",
|
||||
"dependencies": [
|
||||
"@smithy/config-resolver",
|
||||
"@smithy/credential-provider-imds",
|
||||
@@ -2779,7 +2789,7 @@
|
||||
"@smithy/types",
|
||||
"tslib@2.8.1"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.12.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.13.tgz"
|
||||
},
|
||||
"@smithy/util-endpoints@3.2.5": {
|
||||
"integrity": "sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A==",
|
||||
@@ -3649,12 +3659,12 @@
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/color-convert/-/color-convert-2.0.1.tgz"
|
||||
},
|
||||
"color-convert@3.1.2": {
|
||||
"integrity": "sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg==",
|
||||
"color-convert@3.1.3": {
|
||||
"integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==",
|
||||
"dependencies": [
|
||||
"color-name@2.0.2"
|
||||
"color-name@2.1.0"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/color-convert/-/color-convert-3.1.2.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/color-convert/-/color-convert-3.1.3.tgz"
|
||||
},
|
||||
"color-name@1.1.3": {
|
||||
"integrity": "a7d0558bd89c42f795dd42328f740831ca53bc25",
|
||||
@@ -3664,24 +3674,24 @@
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"tarball": "https://verdaccio.lossless.digital/color-name/-/color-name-1.1.4.tgz"
|
||||
},
|
||||
"color-name@2.0.2": {
|
||||
"integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
|
||||
"tarball": "https://verdaccio.lossless.digital/color-name/-/color-name-2.0.2.tgz"
|
||||
"color-name@2.1.0": {
|
||||
"integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
|
||||
"tarball": "https://verdaccio.lossless.digital/color-name/-/color-name-2.1.0.tgz"
|
||||
},
|
||||
"color-string@2.1.2": {
|
||||
"integrity": "sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA==",
|
||||
"color-string@2.1.4": {
|
||||
"integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==",
|
||||
"dependencies": [
|
||||
"color-name@2.0.2"
|
||||
"color-name@2.1.0"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/color-string/-/color-string-2.1.2.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/color-string/-/color-string-2.1.4.tgz"
|
||||
},
|
||||
"color@5.0.2": {
|
||||
"integrity": "sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA==",
|
||||
"color@5.0.3": {
|
||||
"integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==",
|
||||
"dependencies": [
|
||||
"color-convert@3.1.2",
|
||||
"color-convert@3.1.3",
|
||||
"color-string"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/color/-/color-5.0.2.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/color/-/color-5.0.3.tgz"
|
||||
},
|
||||
"combined-stream@1.0.8": {
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
@@ -4351,8 +4361,8 @@
|
||||
"integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==",
|
||||
"tarball": "https://verdaccio.lossless.digital/form-data-encoder/-/form-data-encoder-2.1.4.tgz"
|
||||
},
|
||||
"form-data@4.0.4": {
|
||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||
"form-data@4.0.5": {
|
||||
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
||||
"dependencies": [
|
||||
"asynckit",
|
||||
"combined-stream",
|
||||
@@ -4360,7 +4370,7 @@
|
||||
"hasown",
|
||||
"mime-types@2.1.35"
|
||||
],
|
||||
"tarball": "https://verdaccio.lossless.digital/form-data/-/form-data-4.0.4.tgz"
|
||||
"tarball": "https://verdaccio.lossless.digital/form-data/-/form-data-4.0.5.tgz"
|
||||
},
|
||||
"format@0.2.2": {
|
||||
"integrity": "d6170107e9efdc4ed30c9dc39016df942b5cb58b",
|
||||
@@ -4753,9 +4763,9 @@
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||
"tarball": "https://verdaccio.lossless.digital/ini/-/ini-1.3.8.tgz"
|
||||
},
|
||||
"ip-address@10.0.1": {
|
||||
"integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==",
|
||||
"tarball": "https://verdaccio.lossless.digital/ip-address/-/ip-address-10.0.1.tgz"
|
||||
"ip-address@10.1.0": {
|
||||
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
|
||||
"tarball": "https://verdaccio.lossless.digital/ip-address/-/ip-address-10.1.0.tgz"
|
||||
},
|
||||
"ipaddr.js@1.9.1": {
|
||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
||||
@@ -4878,6 +4888,15 @@
|
||||
"bin": true,
|
||||
"tarball": "https://verdaccio.lossless.digital/js-yaml/-/js-yaml-3.14.1.tgz"
|
||||
},
|
||||
"js-yaml@3.14.2": {
|
||||
"integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
|
||||
"dependencies": [
|
||||
"argparse@1.0.10",
|
||||
"esprima"
|
||||
],
|
||||
"bin": true,
|
||||
"tarball": "https://verdaccio.lossless.digital/js-yaml/-/js-yaml-3.14.2.tgz"
|
||||
},
|
||||
"js-yaml@4.1.1": {
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"dependencies": [
|
||||
@@ -7172,16 +7191,16 @@
|
||||
"workspace": {
|
||||
"packageJson": {
|
||||
"dependencies": [
|
||||
"npm:@git.zone/tsbuild@^2.7.1",
|
||||
"npm:@git.zone/tsrun@^1.6.2",
|
||||
"npm:@git.zone/tstest@^2.7.0",
|
||||
"npm:@git.zone/tsbuild@^3.1.0",
|
||||
"npm:@git.zone/tsrun@2",
|
||||
"npm:@git.zone/tstest@^2.8.1",
|
||||
"npm:@push.rocks/smartenv@6",
|
||||
"npm:@push.rocks/smartpath@6",
|
||||
"npm:@push.rocks/smartpromise@^4.0.4",
|
||||
"npm:@push.rocks/smarturl@^3.1.0",
|
||||
"npm:@types/node@^22.9.0",
|
||||
"npm:agentkeepalive@^4.5.0",
|
||||
"npm:form-data@^4.0.4"
|
||||
"npm:form-data@^4.0.5"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
20
package.json
20
package.json
@@ -1,12 +1,14 @@
|
||||
{
|
||||
"name": "@push.rocks/smartrequest",
|
||||
"version": "4.3.8",
|
||||
"version": "5.0.0",
|
||||
"private": false,
|
||||
"description": "A module for modern HTTP/HTTPS requests with support for form data, file uploads, JSON, binary data, streams, and more.",
|
||||
"exports": {
|
||||
".": "./dist_ts/index.js",
|
||||
"./core_node": "./dist_ts/core_node/index.js",
|
||||
"./core_fetch": "./dist_ts/core_fetch/index.js"
|
||||
"./core_fetch": "./dist_ts/core_fetch/index.js",
|
||||
"./core_bun": "./dist_ts/core_bun/index.js",
|
||||
"./core_deno": "./dist_ts/core_deno/index.js"
|
||||
},
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -30,7 +32,11 @@
|
||||
"keepAlive",
|
||||
"TypeScript",
|
||||
"modern web requests",
|
||||
"drop-in replacement"
|
||||
"drop-in replacement",
|
||||
"Bun",
|
||||
"Deno",
|
||||
"Node.js",
|
||||
"unix sockets"
|
||||
],
|
||||
"author": "Task Venture Capital GmbH",
|
||||
"license": "MIT",
|
||||
@@ -44,12 +50,12 @@
|
||||
"@push.rocks/smartpromise": "^4.0.4",
|
||||
"@push.rocks/smarturl": "^3.1.0",
|
||||
"agentkeepalive": "^4.5.0",
|
||||
"form-data": "^4.0.4"
|
||||
"form-data": "^4.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@git.zone/tsbuild": "^2.7.1",
|
||||
"@git.zone/tsrun": "^1.6.2",
|
||||
"@git.zone/tstest": "^2.7.0",
|
||||
"@git.zone/tsbuild": "^3.1.0",
|
||||
"@git.zone/tsrun": "^2.0.0",
|
||||
"@git.zone/tstest": "^2.8.1",
|
||||
"@types/node": "^22.9.0"
|
||||
},
|
||||
"files": [
|
||||
|
||||
2071
pnpm-lock.yaml
generated
2071
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -4,26 +4,28 @@
|
||||
|
||||
- supports http
|
||||
- supports https
|
||||
- supports unix socks
|
||||
- supports unix sockets on Node.js, Bun, and Deno
|
||||
- supports formData
|
||||
- supports file uploads
|
||||
- supports best practice keepAlive
|
||||
- dedicated functions for working with JSON request/response cycles
|
||||
- written in TypeScript
|
||||
- continuously updated
|
||||
- uses node native http and https modules
|
||||
- supports both Node.js and browser environments
|
||||
- supports Node.js, Bun, Deno, and browser environments with automatic runtime detection
|
||||
- runtime-specific implementations using native APIs (Node http/https, Bun fetch, Deno fetch with HttpClient, browser fetch)
|
||||
- used in modules like @push.rocks/smartproxy and @api.global/typedrequest
|
||||
|
||||
## Architecture Overview (as of v3.0.0 major refactoring)
|
||||
## Architecture Overview (as of v4.x with Bun and Deno support)
|
||||
|
||||
- The project now has a multi-layer architecture with platform abstraction
|
||||
- The project has a multi-layer architecture with runtime abstraction
|
||||
- Base layer (ts/core_base/) contains abstract classes and unified types
|
||||
- Node.js implementation (ts/core_node/) uses native http/https modules
|
||||
- Fetch implementation (ts/core_fetch/) uses Fetch API for browser compatibility
|
||||
- Core module (ts/core/) dynamically selects the appropriate implementation based on environment
|
||||
- Client API (ts/client/) provides a fluent, chainable interface
|
||||
- Legacy API has been completely removed in v3.0.0
|
||||
- Node.js implementation (ts/core_node/) uses native http/https modules with unix socket support
|
||||
- Bun implementation (ts/core_bun/) uses Bun's native fetch with unix socket support via `unix` option
|
||||
- Deno implementation (ts/core_deno/) uses Deno's fetch with unix socket support via HttpClient proxy
|
||||
- Browser implementation (ts/core_fetch/) uses standard Fetch API for browser compatibility
|
||||
- Core module (ts/core/) uses @push.rocks/smartenv to detect runtime and dynamically load appropriate implementation
|
||||
- Client API (ts/client/) provides a fluent, chainable interface that works across all runtimes
|
||||
- Runtime detection order: Deno → Bun → Node.js → Browser (following smartenv detection best practices)
|
||||
|
||||
## Key Components
|
||||
|
||||
@@ -56,6 +58,31 @@
|
||||
- `stream()` returns native web ReadableStream from response.body
|
||||
- `streamNode()` throws error explaining it's not available in browser
|
||||
|
||||
### Core Bun Module (ts/core_bun/)
|
||||
|
||||
- `request.ts`: Bun implementation using native fetch with unix socket support
|
||||
- Uses Bun's `unix` fetch option for unix socket connections
|
||||
- Supports both `unix` and `socketPath` options (converts socketPath to unix)
|
||||
- Handles URL parsing for `http://unix:/path/to/socket:/http/path` format
|
||||
- Throws errors for Node.js specific options (agent)
|
||||
- `response.ts`: Bun-based CoreResponse implementation
|
||||
- `stream()` returns native web ReadableStream from response.body
|
||||
- `streamNode()` throws error (Bun uses web streams; users should use stream() instead)
|
||||
- `types.ts`: Extends base types with IBunRequestOptions including `unix` option
|
||||
|
||||
### Core Deno Module (ts/core_deno/)
|
||||
|
||||
- `request.ts`: Deno implementation using fetch with HttpClient proxy for unix sockets
|
||||
- Creates and caches Deno.HttpClient instances per socket path
|
||||
- Supports both explicit `client` option and automatic client creation from `socketPath`
|
||||
- HttpClient cache prevents creating multiple clients for same socket
|
||||
- Provides `clearClientCache()` static method for cleanup
|
||||
- Throws errors for Node.js specific options (agent)
|
||||
- `response.ts`: Deno-based CoreResponse implementation
|
||||
- `stream()` returns native web ReadableStream from response.body
|
||||
- `streamNode()` throws error (Deno uses web streams, not Node.js streams)
|
||||
- `types.ts`: Extends base types with IDenoRequestOptions including `client` option
|
||||
|
||||
### Core Module (ts/core/)
|
||||
|
||||
- Dynamically loads appropriate implementation based on environment
|
||||
@@ -70,10 +97,15 @@
|
||||
|
||||
### Stream Handling
|
||||
|
||||
- `stream()` method always returns web-style ReadableStream<Uint8Array>
|
||||
- `stream()` method always returns web-style ReadableStream<Uint8Array> across all platforms
|
||||
- In Node.js, converts native streams to web streams
|
||||
- `streamNode()` available only in Node.js environment for native streams
|
||||
- Consistent API across platforms while preserving platform-specific capabilities
|
||||
- `streamNode()` availability by runtime:
|
||||
- **Node.js**: Returns native Node.js ReadableStream (only runtime that supports this)
|
||||
- **Bun**: Throws error (use web streams via stream() instead)
|
||||
- **Deno**: Throws error (Deno uses web streams only)
|
||||
- **Browser**: Throws error (browsers use web streams only)
|
||||
- Consistent API across platforms with web streams as the common denominator
|
||||
- Only Node.js provides native Node.js streams via streamNode()
|
||||
|
||||
### Binary Request Handling
|
||||
|
||||
@@ -85,5 +117,11 @@
|
||||
|
||||
- Use `pnpm test` to run all tests
|
||||
- Tests use @git.zone/tstest/tapbundle for assertions
|
||||
- Separate test files for Node.js (test.node.ts) and browser (test.browser.ts)
|
||||
- Test file naming conventions:
|
||||
- `test.node.ts` - Node.js only tests
|
||||
- `test.bun.ts` - Bun only tests
|
||||
- `test.deno.ts` - Deno only tests
|
||||
- `test.node+bun+deno.ts` - Server-side runtime tests (all three)
|
||||
- `test.browser.ts` or `test.chrome.ts` - Browser tests
|
||||
- Unix socket tests check for Docker socket availability and skip if not present
|
||||
- Browser tests run in headless Chromium via puppeteer
|
||||
|
||||
371
readme.md
371
readme.md
@@ -1,6 +1,6 @@
|
||||
# @push.rocks/smartrequest
|
||||
|
||||
A modern, cross-platform HTTP/HTTPS request library for Node.js and browsers with a unified API, supporting form data, file uploads, JSON, binary data, streams, and unix sockets.
|
||||
A modern, cross-platform HTTP/HTTPS request library for Node.js, Bun, Deno, and browsers with a unified API, supporting form data, file uploads, JSON, binary data, streams, and unix sockets.
|
||||
|
||||
## Install
|
||||
|
||||
@@ -18,26 +18,30 @@ yarn add @push.rocks/smartrequest
|
||||
## Key Features
|
||||
|
||||
- 🚀 **Modern Fetch-like API** - Familiar response methods (`.json()`, `.text()`, `.arrayBuffer()`, `.stream()`)
|
||||
- 🌐 **Cross-Platform** - Works in both Node.js and browsers with a unified API
|
||||
- 🔌 **Unix Socket Support** - Connect to local services like Docker (Node.js only)
|
||||
- 🌐 **Cross-Platform** - Works in Node.js, Bun, Deno, and browsers with a unified API
|
||||
- 🔌 **Unix Socket Support** - Connect to local services like Docker (Node.js, Bun, and Deno)
|
||||
- 📦 **Form Data & File Uploads** - Built-in support for multipart/form-data
|
||||
- 🔁 **Pagination Support** - Multiple strategies (offset, cursor, Link headers)
|
||||
- ⚡ **Keep-Alive Connections** - Efficient connection pooling in Node.js
|
||||
- 🛡️ **TypeScript First** - Full type safety and IntelliSense support
|
||||
- 🎯 **Zero Magic Defaults** - Explicit configuration following fetch API principles
|
||||
- 📡 **Streaming Support** - Stream buffers, files, and custom data without loading into memory
|
||||
- 🔧 **Highly Configurable** - Timeouts, retries, headers, and more
|
||||
- 🔧 **Highly Configurable** - Timeouts, retries, headers, rate limiting, and more
|
||||
|
||||
## Architecture
|
||||
|
||||
SmartRequest v3.0 features a multi-layer architecture that provides consistent behavior across platforms:
|
||||
SmartRequest features a multi-layer architecture that provides consistent behavior across platforms:
|
||||
|
||||
- **Core Base** - Abstract classes and unified types shared across implementations
|
||||
- **Core Node** - Node.js implementation using native http/https modules
|
||||
- **Core Node** - Node.js implementation using native http/https modules with unix socket support
|
||||
- **Core Bun** - Bun implementation using native fetch with unix socket support via `unix` option
|
||||
- **Core Deno** - Deno implementation using fetch with unix socket support via HttpClient proxy
|
||||
- **Core Fetch** - Browser implementation using the Fetch API
|
||||
- **Core** - Dynamic implementation selection based on environment
|
||||
- **Core** - Dynamic runtime detection and implementation selection using @push.rocks/smartenv
|
||||
- **Client** - High-level fluent API for everyday use
|
||||
|
||||
The library automatically detects the runtime environment (Deno, Bun, Node.js, or browser) and loads the appropriate implementation, ensuring optimal performance and native feature support for each platform.
|
||||
|
||||
## Usage
|
||||
|
||||
`@push.rocks/smartrequest` provides a clean, type-safe API inspired by the native fetch API but with additional features needed for modern applications.
|
||||
@@ -178,11 +182,11 @@ async function downloadImage(url: string) {
|
||||
return Buffer.from(buffer); // Convert ArrayBuffer to Buffer if needed
|
||||
}
|
||||
|
||||
// Streaming response (Web Streams API)
|
||||
// Streaming response (Web Streams API - cross-platform)
|
||||
async function streamLargeFile(url: string) {
|
||||
const response = await SmartRequest.create().url(url).get();
|
||||
|
||||
// Get a web-style ReadableStream (works in both Node.js and browsers)
|
||||
// Get a web-style ReadableStream (works everywhere)
|
||||
const stream = response.stream();
|
||||
|
||||
if (stream) {
|
||||
@@ -200,12 +204,14 @@ async function streamLargeFile(url: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Node.js specific stream (only in Node.js environment)
|
||||
// Convert to Node.js stream if needed (Node.js only)
|
||||
async function streamWithNodeApi(url: string) {
|
||||
const response = await SmartRequest.create().url(url).get();
|
||||
|
||||
// Only available in Node.js, throws error in browser
|
||||
const nodeStream = response.streamNode();
|
||||
// Convert web stream to Node.js stream
|
||||
import { Readable } from 'stream';
|
||||
const webStream = response.stream();
|
||||
const nodeStream = Readable.fromWeb(webStream);
|
||||
|
||||
nodeStream.on('data', (chunk) => {
|
||||
console.log(`Received ${chunk.length} bytes of data`);
|
||||
@@ -226,8 +232,7 @@ The response object provides these methods:
|
||||
- `text(): Promise<string>` - Get response as text
|
||||
- `arrayBuffer(): Promise<ArrayBuffer>` - Get response as ArrayBuffer
|
||||
- `stream(): ReadableStream<Uint8Array> | null` - Get web-style ReadableStream (cross-platform)
|
||||
- `streamNode(): NodeJS.ReadableStream` - Get Node.js stream (Node.js only, throws in browser)
|
||||
- `raw(): Response | http.IncomingMessage` - Get the underlying platform response
|
||||
- `raw(): Response | http.IncomingMessage` - Get the underlying platform response object
|
||||
|
||||
Each body method can only be called once per response, similar to the fetch API.
|
||||
|
||||
@@ -312,66 +317,67 @@ import { SmartRequest } from '@push.rocks/smartrequest';
|
||||
import * as fs from 'fs';
|
||||
import { Readable } from 'stream';
|
||||
|
||||
// Stream a Buffer directly
|
||||
// Stream a Buffer directly (works everywhere)
|
||||
async function uploadBuffer() {
|
||||
const buffer = Buffer.from('Hello, World!');
|
||||
|
||||
|
||||
const response = await SmartRequest.create()
|
||||
.url('https://api.example.com/upload')
|
||||
.buffer(buffer, 'text/plain')
|
||||
.post();
|
||||
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
// Stream a file using Node.js streams
|
||||
// Stream using web ReadableStream (cross-platform!)
|
||||
async function uploadWebStream() {
|
||||
const stream = new ReadableStream({
|
||||
start(controller) {
|
||||
const data = new TextEncoder().encode('Stream data');
|
||||
controller.enqueue(data);
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
|
||||
const response = await SmartRequest.create()
|
||||
.url('https://api.example.com/upload')
|
||||
.stream(stream, 'text/plain')
|
||||
.post();
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
// Stream a file using Node.js streams (Node.js only)
|
||||
async function uploadLargeFile(filePath: string) {
|
||||
const fileStream = fs.createReadStream(filePath);
|
||||
|
||||
|
||||
const response = await SmartRequest.create()
|
||||
.url('https://api.example.com/upload')
|
||||
.stream(fileStream, 'application/octet-stream')
|
||||
.post();
|
||||
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
// Stream data from any readable source
|
||||
// Stream data from any readable source (Node.js only)
|
||||
async function streamData(dataSource: Readable) {
|
||||
const response = await SmartRequest.create()
|
||||
.url('https://api.example.com/stream')
|
||||
.stream(dataSource)
|
||||
.post();
|
||||
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
// Advanced: Full control over request streaming (Node.js only)
|
||||
async function customStreaming() {
|
||||
const response = await SmartRequest.create()
|
||||
.url('https://api.example.com/stream')
|
||||
.raw((request) => {
|
||||
// Custom streaming logic - you have full control
|
||||
request.write('chunk1');
|
||||
request.write('chunk2');
|
||||
|
||||
// Stream from another source
|
||||
someReadableStream.pipe(request);
|
||||
})
|
||||
.post();
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
// Send Uint8Array (works in both Node.js and browser)
|
||||
// Send Uint8Array (works everywhere)
|
||||
async function uploadBinaryData() {
|
||||
const data = new Uint8Array([72, 101, 108, 108, 111]); // "Hello"
|
||||
|
||||
|
||||
const response = await SmartRequest.create()
|
||||
.url('https://api.example.com/binary')
|
||||
.buffer(data, 'application/octet-stream')
|
||||
.post();
|
||||
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
```
|
||||
@@ -379,20 +385,15 @@ async function uploadBinaryData() {
|
||||
#### Streaming Methods
|
||||
|
||||
- **`.buffer(data, contentType?)`** - Stream a Buffer or Uint8Array directly
|
||||
- `data`: Buffer (Node.js) or Uint8Array (both platforms) to send
|
||||
- `data`: Buffer (Node.js) or Uint8Array (cross-platform) to send
|
||||
- `contentType`: Optional content type (defaults to 'application/octet-stream')
|
||||
- ✅ Works in both Node.js and browsers
|
||||
- ✅ Works everywhere (Node.js, Bun, Deno, browsers)
|
||||
|
||||
- **`.stream(stream, contentType?)`** - Stream from ReadableStream
|
||||
- `stream`: Web ReadableStream (both platforms) or Node.js stream (Node.js only)
|
||||
- **`.stream(stream, contentType?)`** - Stream from ReadableStream or Node.js stream
|
||||
- `stream`: Web ReadableStream (cross-platform) or Node.js stream (Node.js only)
|
||||
- `contentType`: Optional content type
|
||||
- ✅ Web ReadableStream works in both Node.js and browsers
|
||||
- ⚠️ Node.js streams only work in Node.js environment
|
||||
|
||||
- **`.raw(streamFunc)`** - Advanced control over request streaming
|
||||
- `streamFunc`: Function that receives the raw request object for custom streaming
|
||||
- ❌ **Node.js only** - not supported in browsers
|
||||
- Use for advanced scenarios like chunked transfer encoding
|
||||
- ✅ Web ReadableStream works everywhere (Node.js, Bun, Deno, browsers)
|
||||
- ⚠️ Node.js streams only work in Node.js (automatically converted to web streams in Bun/Deno)
|
||||
|
||||
These methods are particularly useful for:
|
||||
- Uploading large files without loading them into memory
|
||||
@@ -400,12 +401,14 @@ These methods are particularly useful for:
|
||||
- Proxying data between services
|
||||
- Implementing chunked transfer encoding
|
||||
|
||||
### Unix Socket Support (Node.js only)
|
||||
### Unix Socket Support (Node.js, Bun, and Deno)
|
||||
|
||||
SmartRequest supports unix sockets across all server-side runtimes with a unified API:
|
||||
|
||||
```typescript
|
||||
import { SmartRequest } from '@push.rocks/smartrequest';
|
||||
|
||||
// Connect to a service via Unix socket
|
||||
// Connect to a service via Unix socket (works on Node.js, Bun, and Deno)
|
||||
async function queryViaUnixSocket() {
|
||||
const response = await SmartRequest.create()
|
||||
.url('http://unix:/var/run/docker.sock:/v1.24/containers/json')
|
||||
@@ -413,6 +416,57 @@ async function queryViaUnixSocket() {
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
// Alternative: Use socketPath option (works on all server runtimes)
|
||||
async function queryWithSocketPath() {
|
||||
const response = await SmartRequest.create()
|
||||
.url('http://localhost/version')
|
||||
.options({ socketPath: '/var/run/docker.sock' })
|
||||
.get();
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
```
|
||||
|
||||
#### Runtime-Specific Unix Socket APIs
|
||||
|
||||
Each runtime implements unix sockets using its native capabilities:
|
||||
|
||||
**Bun:**
|
||||
```typescript
|
||||
import { CoreRequest } from '@push.rocks/smartrequest/core_bun';
|
||||
|
||||
// Bun uses the native `unix` fetch option
|
||||
const response = await CoreRequest.create('http://localhost/version', {
|
||||
unix: '/var/run/docker.sock'
|
||||
});
|
||||
```
|
||||
|
||||
**Deno:**
|
||||
```typescript
|
||||
import { CoreRequest } from '@push.rocks/smartrequest/core_deno';
|
||||
|
||||
// Deno uses HttpClient with unix socket proxy
|
||||
const client = Deno.createHttpClient({
|
||||
proxy: { url: 'unix:///var/run/docker.sock' }
|
||||
});
|
||||
|
||||
const response = await CoreRequest.create('http://localhost/version', {
|
||||
client
|
||||
});
|
||||
|
||||
// Clean up when done
|
||||
client.close();
|
||||
```
|
||||
|
||||
**Node.js:**
|
||||
```typescript
|
||||
import { CoreRequest } from '@push.rocks/smartrequest/core_node';
|
||||
|
||||
// Node.js uses native socketPath option
|
||||
const response = await CoreRequest.create('http://localhost/version', {
|
||||
socketPath: '/var/run/docker.sock'
|
||||
});
|
||||
```
|
||||
|
||||
### Pagination Support
|
||||
@@ -599,12 +653,61 @@ const response = await SmartRequest.create()
|
||||
.get();
|
||||
```
|
||||
|
||||
### Bun-Specific Options
|
||||
|
||||
When running in Bun, you can use Bun-specific options:
|
||||
|
||||
```typescript
|
||||
const response = await SmartRequest.create()
|
||||
.url('https://api.example.com/data')
|
||||
.options({
|
||||
unix: '/var/run/api.sock', // Unix socket (Bun's native option)
|
||||
keepAlive: true, // Keep-alive support
|
||||
})
|
||||
.get();
|
||||
|
||||
// Bun uses web streams natively
|
||||
const streamResponse = await SmartRequest.create()
|
||||
.url('https://api.example.com/data')
|
||||
.get();
|
||||
|
||||
const webStream = streamResponse.stream(); // ✅ Use web streams in Bun
|
||||
```
|
||||
|
||||
### Deno-Specific Options
|
||||
|
||||
When running in Deno, you can use Deno-specific options:
|
||||
|
||||
```typescript
|
||||
// Custom HttpClient for advanced configuration
|
||||
const client = Deno.createHttpClient({
|
||||
proxy: { url: 'unix:///var/run/api.sock' }
|
||||
});
|
||||
|
||||
const response = await SmartRequest.create()
|
||||
.url('https://api.example.com/data')
|
||||
.options({
|
||||
client, // Custom Deno HttpClient
|
||||
})
|
||||
.get();
|
||||
|
||||
// Remember to clean up clients when done
|
||||
client.close();
|
||||
|
||||
// Deno uses web streams natively
|
||||
const streamResponse = await SmartRequest.create()
|
||||
.url('https://api.example.com/data')
|
||||
.get();
|
||||
|
||||
const webStream = streamResponse.stream(); // ✅ Use web streams in Deno
|
||||
```
|
||||
|
||||
## Complete Example: Building a REST API Client
|
||||
|
||||
Here's a complete example of building a typed API client:
|
||||
|
||||
```typescript
|
||||
import { SmartRequest, type CoreResponse } from '@push.rocks/smartrequest';
|
||||
import { SmartRequest, type ICoreResponse } from '@push.rocks/smartrequest';
|
||||
|
||||
interface User {
|
||||
id: number;
|
||||
@@ -644,6 +747,9 @@ class BlogApiClient {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to delete post: ${response.statusText}`);
|
||||
}
|
||||
|
||||
// Consume the body
|
||||
await response.text();
|
||||
}
|
||||
|
||||
async getAllPosts(userId?: number): Promise<Post[]> {
|
||||
@@ -707,9 +813,158 @@ async function fetchWithErrorHandling(url: string) {
|
||||
}
|
||||
```
|
||||
|
||||
## Migrating from v2.x to v3.x
|
||||
## Migrating from Earlier Versions
|
||||
|
||||
Version 3.0 brings significant architectural improvements and a more consistent API:
|
||||
### From v4.x to v5.x
|
||||
|
||||
Version 5.0 completes the transition to modern web standards by removing Node.js-specific streaming APIs:
|
||||
|
||||
#### **Breaking Changes**
|
||||
|
||||
1. **`.streamNode()` Method Removed**
|
||||
- The `.streamNode()` method has been removed from all response objects
|
||||
- Use the cross-platform `.stream()` method instead, which returns a web `ReadableStream<Uint8Array>`
|
||||
- For Node.js users who need Node.js streams, convert using `Readable.fromWeb()`
|
||||
|
||||
```typescript
|
||||
// ❌ Before (v4.x) - Node.js only
|
||||
const response = await SmartRequest.create().url(url).get();
|
||||
const nodeStream = response.streamNode();
|
||||
|
||||
// ✅ After (v5.x) - Cross-platform
|
||||
import { Readable } from 'stream';
|
||||
|
||||
const response = await SmartRequest.create().url(url).get();
|
||||
const webStream = response.stream();
|
||||
const nodeStream = Readable.fromWeb(webStream); // Convert to Node.js stream
|
||||
```
|
||||
|
||||
2. **Request `.raw()` Method Removed**
|
||||
- The `.raw(streamFunc)` method has been removed from the SmartRequest client
|
||||
- Use `.stream()` with a web `ReadableStream` instead for request body streaming
|
||||
- Node.js users can create web streams from Node.js streams using `Readable.toWeb()`
|
||||
|
||||
```typescript
|
||||
// ❌ Before (v4.x) - Node.js only
|
||||
const response = await SmartRequest.create()
|
||||
.url(url)
|
||||
.raw((request) => {
|
||||
request.write('chunk1');
|
||||
request.write('chunk2');
|
||||
request.end();
|
||||
})
|
||||
.post();
|
||||
|
||||
// ✅ After (v5.x) - Cross-platform
|
||||
const stream = new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(new TextEncoder().encode('chunk1'));
|
||||
controller.enqueue(new TextEncoder().encode('chunk2'));
|
||||
controller.close();
|
||||
}
|
||||
});
|
||||
|
||||
const response = await SmartRequest.create()
|
||||
.url(url)
|
||||
.stream(stream)
|
||||
.post();
|
||||
|
||||
// Or convert from Node.js stream (Node.js only)
|
||||
import { Readable } from 'stream';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const nodeStream = fs.createReadStream('file.txt');
|
||||
const webStream = Readable.toWeb(nodeStream);
|
||||
|
||||
const response = await SmartRequest.create()
|
||||
.url(url)
|
||||
.stream(webStream)
|
||||
.post();
|
||||
```
|
||||
|
||||
3. **Response `.raw()` Method Preserved**
|
||||
- The `response.raw()` method is still available for accessing platform-specific response objects
|
||||
- Returns `http.IncomingMessage` in Node.js or `Response` in other runtimes
|
||||
- Use for advanced scenarios requiring access to raw platform objects
|
||||
|
||||
```typescript
|
||||
// ✅ Still works in v5.x
|
||||
const response = await SmartRequest.create().url(url).get();
|
||||
const rawResponse = response.raw(); // http.IncomingMessage or Response
|
||||
```
|
||||
|
||||
#### **Migration Guide**
|
||||
|
||||
**For Response Streaming:**
|
||||
|
||||
```typescript
|
||||
// Before (v4.x)
|
||||
const response = await SmartRequest.create().url(url).get();
|
||||
const nodeStream = response.streamNode();
|
||||
|
||||
nodeStream.on('data', (chunk) => {
|
||||
console.log(`Received ${chunk.length} bytes`);
|
||||
});
|
||||
|
||||
// After (v5.x) - Option 1: Use web streams directly
|
||||
const response = await SmartRequest.create().url(url).get();
|
||||
const webStream = response.stream();
|
||||
|
||||
if (webStream) {
|
||||
const reader = webStream.getReader();
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
console.log(`Received ${value.length} bytes`);
|
||||
}
|
||||
reader.releaseLock();
|
||||
}
|
||||
|
||||
// After (v5.x) - Option 2: Convert to Node.js stream (Node.js only)
|
||||
import { Readable } from 'stream';
|
||||
|
||||
const response = await SmartRequest.create().url(url).get();
|
||||
const webStream = response.stream();
|
||||
const nodeStream = Readable.fromWeb(webStream);
|
||||
|
||||
nodeStream.on('data', (chunk) => {
|
||||
console.log(`Received ${chunk.length} bytes`);
|
||||
});
|
||||
```
|
||||
|
||||
**For Request Streaming:**
|
||||
|
||||
Node.js streams are still accepted by the `.stream()` method and automatically converted internally. No changes required for most use cases:
|
||||
|
||||
```typescript
|
||||
// ✅ Still works in v5.x
|
||||
import * as fs from 'fs';
|
||||
|
||||
const fileStream = fs.createReadStream('large-file.bin');
|
||||
const response = await SmartRequest.create()
|
||||
.url('https://api.example.com/upload')
|
||||
.stream(fileStream, 'application/octet-stream')
|
||||
.post();
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- ✅ True cross-platform compatibility
|
||||
- ✅ Modern web standards
|
||||
- ✅ Cleaner API surface
|
||||
- ✅ Single streaming approach works everywhere
|
||||
|
||||
### From v3.x to v4.x
|
||||
|
||||
Version 4.0 adds comprehensive cross-platform support:
|
||||
|
||||
1. **Multi-Runtime Support**: Now works natively in Node.js, Bun, Deno, and browsers
|
||||
2. **Unix Sockets Everywhere**: Unix socket support added for Bun and Deno
|
||||
3. **Web Streams**: Full support for web ReadableStream across all platforms
|
||||
4. **Automatic Runtime Detection**: No configuration needed - works everywhere automatically
|
||||
|
||||
### From v2.x to v3.x
|
||||
|
||||
Version 3.0 brought significant architectural improvements:
|
||||
|
||||
1. **Legacy API Removed**: The function-based API (getJson, postJson, etc.) has been removed. Use SmartRequest instead.
|
||||
2. **Unified Response API**: All responses now use the same fetch-like interface regardless of platform.
|
||||
|
||||
@@ -1,71 +1,58 @@
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
import * as fs from 'node:fs';
|
||||
import { SmartRequest } from '../ts/index.js';
|
||||
|
||||
// Cross-platform tests using web-standard APIs only
|
||||
|
||||
tap.test('should send a buffer using buffer() method', async () => {
|
||||
const testBuffer = Buffer.from('Hello, World!');
|
||||
|
||||
|
||||
const smartRequest = SmartRequest.create()
|
||||
.url('https://httpbin.org/post')
|
||||
.buffer(testBuffer, 'text/plain')
|
||||
.method('POST');
|
||||
|
||||
|
||||
const response = await smartRequest.post();
|
||||
const data = await response.json();
|
||||
|
||||
|
||||
expect(data).toHaveProperty('data');
|
||||
expect(data.data).toEqual('Hello, World!');
|
||||
expect(data.headers['Content-Type']).toEqual('text/plain');
|
||||
});
|
||||
|
||||
tap.test('should send a stream using stream() method', async () => {
|
||||
// Create a simple readable stream
|
||||
const { Readable } = await import('stream');
|
||||
tap.test('should send a web ReadableStream using stream() method', async () => {
|
||||
const testData = 'Stream data test';
|
||||
const stream = Readable.from([testData]);
|
||||
|
||||
|
||||
// Use web-standard ReadableStream (works on all platforms)
|
||||
const stream = new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(new TextEncoder().encode(testData));
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
|
||||
const smartRequest = SmartRequest.create()
|
||||
.url('https://httpbin.org/post')
|
||||
.stream(stream, 'text/plain')
|
||||
.method('POST');
|
||||
|
||||
const response = await smartRequest.post();
|
||||
const data = await response.json();
|
||||
|
||||
expect(data).toHaveProperty('data');
|
||||
expect(data.data).toEqual(testData);
|
||||
});
|
||||
|
||||
tap.test('should handle raw streaming with custom function', async () => {
|
||||
const testData = 'Custom raw stream data';
|
||||
|
||||
const smartRequest = SmartRequest.create()
|
||||
.url('https://httpbin.org/post')
|
||||
.raw((request) => {
|
||||
// Custom streaming logic
|
||||
request.write(testData);
|
||||
request.end();
|
||||
})
|
||||
.method('POST');
|
||||
|
||||
const response = await smartRequest.post();
|
||||
const data = await response.json();
|
||||
|
||||
|
||||
expect(data).toHaveProperty('data');
|
||||
expect(data.data).toEqual(testData);
|
||||
});
|
||||
|
||||
tap.test('should send Uint8Array using buffer() method', async () => {
|
||||
const testData = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" in ASCII
|
||||
|
||||
|
||||
const smartRequest = SmartRequest.create()
|
||||
.url('https://httpbin.org/post')
|
||||
.buffer(testData, 'application/octet-stream')
|
||||
.method('POST');
|
||||
|
||||
|
||||
const response = await smartRequest.post();
|
||||
const data = await response.json();
|
||||
|
||||
|
||||
// Just verify that data was sent
|
||||
expect(data).toHaveProperty('data');
|
||||
expect(data.headers['Content-Type']).toEqual('application/octet-stream');
|
||||
@@ -1,27 +1,68 @@
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
import { SmartRequest } from '../ts/index.js';
|
||||
import { Readable } from 'stream';
|
||||
|
||||
tap.test('should have streamNode() method available', async () => {
|
||||
tap.test('should have stream() method that returns web ReadableStream', async () => {
|
||||
const response = await SmartRequest.create()
|
||||
.url('https://httpbin.org/get')
|
||||
.get();
|
||||
|
||||
// Verify streamNode() method exists
|
||||
expect(response.streamNode).toBeDefined();
|
||||
expect(typeof response.streamNode).toEqual('function');
|
||||
|
||||
// In Node.js, it should return a stream
|
||||
const nodeStream = response.streamNode();
|
||||
// Verify stream() method exists
|
||||
expect(response.stream).toBeDefined();
|
||||
expect(typeof response.stream).toEqual('function');
|
||||
|
||||
// Get web stream
|
||||
const webStream = response.stream();
|
||||
expect(webStream).toBeDefined();
|
||||
|
||||
// Verify it's a web ReadableStream
|
||||
expect(typeof webStream.getReader).toEqual('function');
|
||||
expect(typeof webStream.cancel).toEqual('function');
|
||||
|
||||
// Convert to Node.js stream using Readable.fromWeb()
|
||||
// Known TypeScript limitation: @types/node ReadableStream differs from web ReadableStream
|
||||
const nodeStream = Readable.fromWeb(webStream as any);
|
||||
expect(nodeStream).toBeDefined();
|
||||
|
||||
|
||||
// Verify it's a Node.js readable stream
|
||||
expect(typeof nodeStream.pipe).toEqual('function');
|
||||
expect(typeof nodeStream.on).toEqual('function');
|
||||
|
||||
|
||||
// Consume the stream to avoid hanging
|
||||
nodeStream.resume();
|
||||
});
|
||||
|
||||
tap.test('should convert web stream to Node.js stream correctly', async () => {
|
||||
const response = await SmartRequest.create()
|
||||
.url('https://httpbin.org/get')
|
||||
.get();
|
||||
|
||||
const webStream = response.stream();
|
||||
const nodeStream = Readable.fromWeb(webStream as any);
|
||||
|
||||
export default tap.start();
|
||||
// Collect data from stream
|
||||
const chunks: Buffer[] = [];
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
nodeStream.on('data', (chunk) => {
|
||||
chunks.push(chunk);
|
||||
});
|
||||
|
||||
nodeStream.on('end', () => {
|
||||
resolve();
|
||||
});
|
||||
|
||||
nodeStream.on('error', reject);
|
||||
});
|
||||
|
||||
// Verify we received data
|
||||
const data = Buffer.concat(chunks);
|
||||
expect(data.length).toBeGreaterThan(0);
|
||||
|
||||
// Verify it's valid JSON
|
||||
const json = JSON.parse(data.toString('utf-8'));
|
||||
expect(json).toBeDefined();
|
||||
expect(json.url).toEqual('https://httpbin.org/get');
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
|
||||
101
test/test.unixsocket.bun.ts
Normal file
101
test/test.unixsocket.bun.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
import { SmartRequest } from '../ts/client/index.js';
|
||||
import { CoreRequest } from '../ts/core_bun/index.js';
|
||||
|
||||
// Check if Docker socket exists (common unix socket for testing)
|
||||
const dockerSocketPath = '/var/run/docker.sock';
|
||||
let dockerAvailable = false;
|
||||
|
||||
try {
|
||||
const file = Bun.file(dockerSocketPath);
|
||||
dockerAvailable = await file.exists();
|
||||
} catch (error) {
|
||||
console.log(
|
||||
'Docker socket not available - skipping unix socket tests. To enable, ensure Docker is running.',
|
||||
);
|
||||
}
|
||||
|
||||
tap.test('bun: should detect unix socket URLs correctly', async () => {
|
||||
expect(CoreRequest.isUnixSocket('unix:/var/run/docker.sock:/version')).toBeTrue();
|
||||
expect(CoreRequest.isUnixSocket('http://unix:/var/run/docker.sock:/version')).toBeTrue();
|
||||
expect(CoreRequest.isUnixSocket('https://unix:/var/run/docker.sock:/version')).toBeTrue();
|
||||
expect(CoreRequest.isUnixSocket('http://example.com')).toBeFalse();
|
||||
expect(CoreRequest.isUnixSocket('https://example.com')).toBeFalse();
|
||||
});
|
||||
|
||||
tap.test('bun: should parse unix socket URLs correctly', async () => {
|
||||
const result = CoreRequest.parseUnixSocketUrl('unix:/var/run/docker.sock:/v1.24/version');
|
||||
expect(result.socketPath).toEqual('/var/run/docker.sock');
|
||||
expect(result.path).toEqual('/v1.24/version');
|
||||
});
|
||||
|
||||
if (dockerAvailable) {
|
||||
tap.test('bun: should connect to Docker via unix socket (unix: protocol)', async () => {
|
||||
const response = await SmartRequest.create()
|
||||
.url('http://unix:/var/run/docker.sock:/version')
|
||||
.get();
|
||||
|
||||
expect(response.ok).toBeTrue();
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
const body = await response.json();
|
||||
expect(body).toHaveProperty('Version');
|
||||
console.log(`Docker version: ${body.Version}`);
|
||||
});
|
||||
|
||||
tap.test('bun: should connect to Docker via socketPath option', async () => {
|
||||
const response = await CoreRequest.create('http://localhost/version', {
|
||||
socketPath: '/var/run/docker.sock',
|
||||
});
|
||||
|
||||
expect(response.ok).toBeTrue();
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
const body = await response.json();
|
||||
expect(body).toHaveProperty('Version');
|
||||
});
|
||||
|
||||
tap.test('bun: should connect to Docker via unix option', async () => {
|
||||
const response = await CoreRequest.create('http://localhost/version', {
|
||||
unix: '/var/run/docker.sock',
|
||||
});
|
||||
|
||||
expect(response.ok).toBeTrue();
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
const body = await response.json();
|
||||
expect(body).toHaveProperty('Version');
|
||||
});
|
||||
|
||||
tap.test('bun: should handle unix socket with query parameters', async () => {
|
||||
const response = await SmartRequest.create()
|
||||
.url('http://unix:/var/run/docker.sock:/containers/json')
|
||||
.query({ all: 'true' })
|
||||
.get();
|
||||
|
||||
expect(response.ok).toBeTrue();
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
const body = await response.json();
|
||||
expect(Array.isArray(body)).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('bun: should handle unix socket with POST requests', async () => {
|
||||
// Test POST to Docker API (this specific endpoint may require permissions)
|
||||
const response = await SmartRequest.create()
|
||||
.url('http://unix:/var/run/docker.sock:/containers/json')
|
||||
.query({ all: 'true', limit: '1' })
|
||||
.get();
|
||||
|
||||
expect(response.status).toBeGreaterThanOrEqual(200);
|
||||
expect(response.status).toBeLessThan(500);
|
||||
|
||||
await response.text(); // Consume body
|
||||
});
|
||||
} else {
|
||||
tap.skip.test(
|
||||
'bun: unix socket tests skipped - Docker socket not available',
|
||||
);
|
||||
}
|
||||
|
||||
export default tap.start();
|
||||
154
test/test.unixsocket.deno.ts
Normal file
154
test/test.unixsocket.deno.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
import { SmartRequest } from '../ts/client/index.js';
|
||||
import { CoreRequest } from '../ts/core_deno/index.js';
|
||||
|
||||
// Check if Docker socket exists (common unix socket for testing)
|
||||
const dockerSocketPath = '/var/run/docker.sock';
|
||||
let dockerAvailable = false;
|
||||
|
||||
try {
|
||||
const fileInfo = await Deno.stat(dockerSocketPath);
|
||||
dockerAvailable = fileInfo.isFile || fileInfo.isSymlink;
|
||||
} catch (error) {
|
||||
console.log(
|
||||
'Docker socket not available - skipping unix socket tests. To enable, ensure Docker is running.',
|
||||
);
|
||||
}
|
||||
|
||||
tap.test('deno: should detect unix socket URLs correctly', async () => {
|
||||
expect(CoreRequest.isUnixSocket('unix:/var/run/docker.sock:/version')).toBeTrue();
|
||||
expect(CoreRequest.isUnixSocket('http://unix:/var/run/docker.sock:/version')).toBeTrue();
|
||||
expect(CoreRequest.isUnixSocket('https://unix:/var/run/docker.sock:/version')).toBeTrue();
|
||||
expect(CoreRequest.isUnixSocket('http://example.com')).toBeFalse();
|
||||
expect(CoreRequest.isUnixSocket('https://example.com')).toBeFalse();
|
||||
});
|
||||
|
||||
tap.test('deno: should parse unix socket URLs correctly', async () => {
|
||||
const result = CoreRequest.parseUnixSocketUrl('unix:/var/run/docker.sock:/v1.24/version');
|
||||
expect(result.socketPath).toEqual('/var/run/docker.sock');
|
||||
expect(result.path).toEqual('/v1.24/version');
|
||||
});
|
||||
|
||||
if (dockerAvailable) {
|
||||
tap.test('deno: should connect to Docker via unix socket (unix: protocol)', async () => {
|
||||
const response = await SmartRequest.create()
|
||||
.url('http://unix:/var/run/docker.sock:/version')
|
||||
.get();
|
||||
|
||||
expect(response.ok).toBeTrue();
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
const body = await response.json();
|
||||
expect(body).toHaveProperty('Version');
|
||||
console.log(`Docker version: ${body.Version}`);
|
||||
});
|
||||
|
||||
tap.test('deno: should connect to Docker via socketPath option', async () => {
|
||||
const response = await CoreRequest.create('http://localhost/version', {
|
||||
socketPath: '/var/run/docker.sock',
|
||||
});
|
||||
|
||||
expect(response.ok).toBeTrue();
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
const body = await response.json();
|
||||
expect(body).toHaveProperty('Version');
|
||||
});
|
||||
|
||||
tap.test('deno: should connect to Docker via HttpClient', async () => {
|
||||
const client = Deno.createHttpClient({
|
||||
proxy: {
|
||||
url: 'unix:///var/run/docker.sock',
|
||||
},
|
||||
});
|
||||
|
||||
const response = await CoreRequest.create('http://localhost/version', {
|
||||
client,
|
||||
});
|
||||
|
||||
expect(response.ok).toBeTrue();
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
const body = await response.json();
|
||||
expect(body).toHaveProperty('Version');
|
||||
|
||||
// Clean up client
|
||||
client.close();
|
||||
});
|
||||
|
||||
tap.test('deno: should handle unix socket with query parameters', async () => {
|
||||
const response = await SmartRequest.create()
|
||||
.url('http://unix:/var/run/docker.sock:/containers/json')
|
||||
.query({ all: 'true' })
|
||||
.get();
|
||||
|
||||
expect(response.ok).toBeTrue();
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
const body = await response.json();
|
||||
expect(Array.isArray(body)).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('deno: should handle unix socket with POST requests', async () => {
|
||||
// Test POST to Docker API (this specific endpoint may require permissions)
|
||||
const response = await SmartRequest.create()
|
||||
.url('http://unix:/var/run/docker.sock:/containers/json')
|
||||
.query({ all: 'true', limit: '1' })
|
||||
.get();
|
||||
|
||||
expect(response.status).toBeGreaterThanOrEqual(200);
|
||||
expect(response.status).toBeLessThan(500);
|
||||
|
||||
await response.text(); // Consume body
|
||||
});
|
||||
|
||||
tap.test('deno: should cache HttpClient for reuse', async () => {
|
||||
// First request creates a client
|
||||
const response1 = await SmartRequest.create()
|
||||
.url('http://unix:/var/run/docker.sock:/version')
|
||||
.get();
|
||||
|
||||
expect(response1.ok).toBeTrue();
|
||||
await response1.text();
|
||||
|
||||
// Second request should reuse the cached client
|
||||
const response2 = await SmartRequest.create()
|
||||
.url('http://unix:/var/run/docker.sock:/version')
|
||||
.get();
|
||||
|
||||
expect(response2.ok).toBeTrue();
|
||||
await response2.text();
|
||||
|
||||
// Clean up cache
|
||||
CoreRequest.clearClientCache();
|
||||
});
|
||||
|
||||
tap.test('deno: should clear HttpClient cache', async () => {
|
||||
const response = await SmartRequest.create()
|
||||
.url('http://unix:/var/run/docker.sock:/version')
|
||||
.get();
|
||||
|
||||
expect(response.ok).toBeTrue();
|
||||
await response.text();
|
||||
|
||||
// Clear cache - should not throw
|
||||
CoreRequest.clearClientCache();
|
||||
|
||||
// Subsequent request should create new client
|
||||
const response2 = await SmartRequest.create()
|
||||
.url('http://unix:/var/run/docker.sock:/version')
|
||||
.get();
|
||||
|
||||
expect(response2.ok).toBeTrue();
|
||||
await response2.text();
|
||||
|
||||
// Clean up
|
||||
CoreRequest.clearClientCache();
|
||||
});
|
||||
} else {
|
||||
tap.skip.test(
|
||||
'deno: unix socket tests skipped - Docker socket not available',
|
||||
);
|
||||
}
|
||||
|
||||
export default tap.start();
|
||||
90
test/test.unixsocket.node.ts
Normal file
90
test/test.unixsocket.node.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
import * as plugins from '../ts/core_node/plugins.js';
|
||||
import { SmartRequest } from '../ts/client/index.js';
|
||||
import { CoreRequest } from '../ts/core_node/index.js';
|
||||
|
||||
// Check if Docker socket exists (common unix socket for testing)
|
||||
const dockerSocketPath = '/var/run/docker.sock';
|
||||
let dockerAvailable = false;
|
||||
|
||||
try {
|
||||
await plugins.fs.promises.access(dockerSocketPath, plugins.fs.constants.R_OK);
|
||||
dockerAvailable = true;
|
||||
} catch (error) {
|
||||
console.log(
|
||||
'Docker socket not available - skipping unix socket tests. To enable, ensure Docker is running.',
|
||||
);
|
||||
}
|
||||
|
||||
tap.test('node: should detect unix socket URLs correctly', async () => {
|
||||
expect(CoreRequest.isUnixSocket('unix:/var/run/docker.sock:/version')).toBeTrue();
|
||||
expect(CoreRequest.isUnixSocket('http://unix:/var/run/docker.sock:/version')).toBeTrue();
|
||||
expect(CoreRequest.isUnixSocket('https://unix:/var/run/docker.sock:/version')).toBeTrue();
|
||||
expect(CoreRequest.isUnixSocket('http://example.com')).toBeFalse();
|
||||
expect(CoreRequest.isUnixSocket('https://example.com')).toBeFalse();
|
||||
});
|
||||
|
||||
tap.test('node: should parse unix socket URLs correctly', async () => {
|
||||
const result = CoreRequest.parseUnixSocketUrl('unix:/var/run/docker.sock:/v1.24/version');
|
||||
expect(result.socketPath).toEqual('/var/run/docker.sock');
|
||||
expect(result.path).toEqual('/v1.24/version');
|
||||
});
|
||||
|
||||
if (dockerAvailable) {
|
||||
tap.test('node: should connect to Docker via unix socket (unix: protocol)', async () => {
|
||||
const response = await SmartRequest.create()
|
||||
.url('http://unix:/var/run/docker.sock:/version')
|
||||
.get();
|
||||
|
||||
expect(response.ok).toBeTrue();
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
const body = await response.json();
|
||||
expect(body).toHaveProperty('Version');
|
||||
console.log(`Docker version: ${body.Version}`);
|
||||
});
|
||||
|
||||
tap.test('node: should connect to Docker via socketPath option', async () => {
|
||||
const response = await CoreRequest.create('http://localhost/version', {
|
||||
socketPath: '/var/run/docker.sock',
|
||||
});
|
||||
|
||||
expect(response.ok).toBeTrue();
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
const body = await response.json();
|
||||
expect(body).toHaveProperty('Version');
|
||||
});
|
||||
|
||||
tap.test('node: should handle unix socket with query parameters', async () => {
|
||||
const response = await SmartRequest.create()
|
||||
.url('http://unix:/var/run/docker.sock:/containers/json')
|
||||
.query({ all: 'true' })
|
||||
.get();
|
||||
|
||||
expect(response.ok).toBeTrue();
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
const body = await response.json();
|
||||
expect(Array.isArray(body)).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('node: should handle unix socket with POST requests', async () => {
|
||||
// Test POST to Docker API (this specific endpoint may require permissions)
|
||||
const response = await SmartRequest.create()
|
||||
.url('http://unix:/var/run/docker.sock:/containers/json')
|
||||
.query({ all: 'true', limit: '1' })
|
||||
.get();
|
||||
|
||||
expect(response.status).toBeGreaterThanOrEqual(200);
|
||||
expect(response.status).toBeLessThan(500);
|
||||
|
||||
await response.text(); // Consume body
|
||||
});
|
||||
} else {
|
||||
tap.skip.test(
|
||||
'node: unix socket tests skipped - Docker socket not available',
|
||||
);
|
||||
}
|
||||
|
||||
export default tap.start();
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartrequest',
|
||||
version: '4.3.8',
|
||||
version: '5.0.0',
|
||||
description: 'A module for modern HTTP/HTTPS requests with support for form data, file uploads, JSON, binary data, streams, and more.'
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import type {
|
||||
ResponseType,
|
||||
FormField,
|
||||
RateLimitConfig,
|
||||
RawStreamFunction,
|
||||
} from './types/common.js';
|
||||
import {
|
||||
type TPaginationConfig,
|
||||
@@ -142,12 +141,12 @@ export class SmartRequest<T = any> {
|
||||
if (!this._options.headers) {
|
||||
this._options.headers = {};
|
||||
}
|
||||
|
||||
|
||||
// Set content type if provided
|
||||
if (contentType) {
|
||||
this._options.headers['Content-Type'] = contentType;
|
||||
}
|
||||
|
||||
|
||||
// Check if it's a Node.js stream (has pipe method)
|
||||
if ('pipe' in stream && typeof (stream as any).pipe === 'function') {
|
||||
// For Node.js streams, we need to use a custom approach
|
||||
@@ -157,18 +156,7 @@ export class SmartRequest<T = any> {
|
||||
// For web ReadableStream, pass directly
|
||||
this._options.requestBody = stream;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a custom function to handle raw request streaming
|
||||
* This gives full control over the request body streaming
|
||||
* Note: Only works in Node.js environment, not supported in browsers
|
||||
*/
|
||||
raw(streamFunc: RawStreamFunction): this {
|
||||
// Store the raw streaming function to be used later
|
||||
(this._options as any).__rawStreamFunc = streamFunc;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -440,22 +428,21 @@ export class SmartRequest<T = any> {
|
||||
// Main retry loop
|
||||
for (let attempt = 0; attempt <= this._retries; attempt++) {
|
||||
try {
|
||||
// Check if we have a Node.js stream or raw function that needs special handling
|
||||
// Check if we have a Node.js stream that needs special handling
|
||||
let requestDataFunc = null;
|
||||
if ((this._options as any).__nodeStream) {
|
||||
const nodeStream = (this._options as any).__nodeStream;
|
||||
requestDataFunc = (req: any) => {
|
||||
nodeStream.pipe(req);
|
||||
};
|
||||
// Remove the temporary stream reference
|
||||
delete (this._options as any).__nodeStream;
|
||||
} else if ((this._options as any).__rawStreamFunc) {
|
||||
requestDataFunc = (this._options as any).__rawStreamFunc;
|
||||
// Remove the temporary function reference
|
||||
delete (this._options as any).__rawStreamFunc;
|
||||
// Don't delete __nodeStream yet - let CoreRequest implementations handle it
|
||||
// Node.js will use requestDataFunc, Bun/Deno will convert the stream
|
||||
}
|
||||
|
||||
|
||||
const request = new CoreRequest(this._url, this._options as any, requestDataFunc);
|
||||
|
||||
// Clean up temporary properties after CoreRequest has been created
|
||||
delete (this._options as any).__nodeStream;
|
||||
const response = (await request.fire()) as ICoreResponse<R>;
|
||||
|
||||
// Check for 429 status if rate limit handling is enabled
|
||||
|
||||
@@ -66,9 +66,3 @@ export interface RateLimitConfig {
|
||||
backoffFactor?: number; // Exponential backoff factor (default: 2)
|
||||
onRateLimit?: (attempt: number, waitTime: number) => void; // Callback for rate limit events
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw streaming function for advanced request body control
|
||||
* Note: The request parameter type depends on the environment (Node.js ClientRequest or fetch Request)
|
||||
*/
|
||||
export type RawStreamFunction = (request: any) => void;
|
||||
|
||||
@@ -5,12 +5,22 @@ export * from '../core_base/types.js';
|
||||
|
||||
const smartenvInstance = new plugins.smartenv.Smartenv();
|
||||
|
||||
// Dynamically load the appropriate implementation
|
||||
// Dynamically load the appropriate implementation based on runtime
|
||||
let CoreRequest: any;
|
||||
let CoreResponse: any;
|
||||
|
||||
if (smartenvInstance.isNode) {
|
||||
// In Node.js, load the node implementation
|
||||
if (smartenvInstance.isDeno) {
|
||||
// In Deno, load the Deno implementation with HttpClient-based unix socket support
|
||||
const impl = await import('../core_deno/index.js');
|
||||
CoreRequest = impl.CoreRequest;
|
||||
CoreResponse = impl.CoreResponse;
|
||||
} else if (smartenvInstance.isBun) {
|
||||
// In Bun, load the Bun implementation with native fetch unix socket support
|
||||
const impl = await import('../core_bun/index.js');
|
||||
CoreRequest = impl.CoreRequest;
|
||||
CoreResponse = impl.CoreResponse;
|
||||
} else if (smartenvInstance.isNode) {
|
||||
// In Node.js, load the Node.js implementation with native http/https unix socket support
|
||||
const modulePath = plugins.smartpath.join(
|
||||
plugins.smartpath.dirname(import.meta.url),
|
||||
'../core_node/index.js',
|
||||
@@ -19,7 +29,7 @@ if (smartenvInstance.isNode) {
|
||||
CoreRequest = impl.CoreRequest;
|
||||
CoreResponse = impl.CoreResponse;
|
||||
} else {
|
||||
// In browser, load the fetch implementation
|
||||
// In browser, load the fetch implementation (no unix socket support)
|
||||
const impl = await import('../core_fetch/index.js');
|
||||
CoreRequest = impl.CoreRequest;
|
||||
CoreResponse = impl.CoreResponse;
|
||||
|
||||
@@ -17,10 +17,28 @@ export abstract class CoreRequest<
|
||||
|
||||
/**
|
||||
* Parses socket path and route from unix socket URL
|
||||
* Handles both full URLs (http://unix:/path/to/socket:/route) and pre-stripped paths (unix:/path/to/socket:/route)
|
||||
* Returns clean file system path for socketPath (e.g., /var/run/docker.sock)
|
||||
*/
|
||||
static parseUnixSocketUrl(url: string): { socketPath: string; path: string } {
|
||||
// Strip http:// or https:// prefix if present
|
||||
// This makes the method work with both full URLs and pre-stripped paths
|
||||
let cleanUrl = url;
|
||||
if (cleanUrl.startsWith('http://')) {
|
||||
cleanUrl = cleanUrl.substring('http://'.length);
|
||||
} else if (cleanUrl.startsWith('https://')) {
|
||||
cleanUrl = cleanUrl.substring('https://'.length);
|
||||
}
|
||||
|
||||
// Strip unix: prefix if present to get clean file system path
|
||||
if (cleanUrl.startsWith('unix:')) {
|
||||
cleanUrl = cleanUrl.substring('unix:'.length);
|
||||
}
|
||||
|
||||
// Parse the socket path and HTTP path
|
||||
// Format: /path/to/socket:/route/path
|
||||
const parseRegex = /(.*):(.*)/;
|
||||
const result = parseRegex.exec(url);
|
||||
const result = parseRegex.exec(cleanUrl);
|
||||
return {
|
||||
socketPath: result[1],
|
||||
path: result[2],
|
||||
|
||||
@@ -42,9 +42,4 @@ export abstract class CoreResponse<T = any> implements types.ICoreResponse<T> {
|
||||
* Get response as a web-style ReadableStream
|
||||
*/
|
||||
abstract stream(): ReadableStream<Uint8Array> | null;
|
||||
|
||||
/**
|
||||
* Get response as a Node.js stream (throws in browser)
|
||||
*/
|
||||
abstract streamNode(): NodeJS.ReadableStream | never;
|
||||
}
|
||||
|
||||
@@ -86,5 +86,4 @@ export interface ICoreResponse<T = any> {
|
||||
text(): Promise<string>;
|
||||
arrayBuffer(): Promise<ArrayBuffer>;
|
||||
stream(): ReadableStream<Uint8Array> | null; // Always returns web-style stream
|
||||
streamNode(): NodeJS.ReadableStream | never; // Returns Node.js stream or throws in browser
|
||||
}
|
||||
|
||||
3
ts/core_bun/index.ts
Normal file
3
ts/core_bun/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
// Core Bun exports - Bun's native fetch implementation with unix socket support
|
||||
export * from './response.js';
|
||||
export { CoreRequest } from './request.js';
|
||||
249
ts/core_bun/request.ts
Normal file
249
ts/core_bun/request.ts
Normal file
@@ -0,0 +1,249 @@
|
||||
import * as types from './types.js';
|
||||
import { CoreResponse } from './response.js';
|
||||
import { CoreRequest as AbstractCoreRequest } from '../core_base/request.js';
|
||||
|
||||
/**
|
||||
* Bun implementation of Core Request class using native fetch with unix socket support
|
||||
*/
|
||||
export class CoreRequest extends AbstractCoreRequest<
|
||||
types.IBunRequestOptions,
|
||||
CoreResponse
|
||||
> {
|
||||
private timeoutId: ReturnType<typeof setTimeout> | null = null;
|
||||
private abortController: AbortController | null = null;
|
||||
private requestDataFunc: ((req: any) => void) | null;
|
||||
|
||||
constructor(
|
||||
url: string,
|
||||
options: types.IBunRequestOptions = {},
|
||||
requestDataFunc: ((req: any) => void) | null = null,
|
||||
) {
|
||||
super(url, options);
|
||||
this.requestDataFunc = requestDataFunc;
|
||||
|
||||
// Check for unsupported Node.js-specific options
|
||||
if (options.agent) {
|
||||
throw new Error(
|
||||
'Node.js specific option (agent) is not supported in Bun implementation',
|
||||
);
|
||||
}
|
||||
|
||||
// Handle Node.js stream conversion if requestDataFunc is provided
|
||||
if (requestDataFunc && (options as any).__nodeStream) {
|
||||
// Convert Node.js stream to web ReadableStream for Bun
|
||||
const nodeStream = (options as any).__nodeStream;
|
||||
|
||||
// Bun can handle Node.js streams via Readable.toWeb if available
|
||||
// Or we can create a web stream that reads from the Node stream
|
||||
if (typeof (nodeStream as any).toWeb === 'function') {
|
||||
this.options.requestBody = (nodeStream as any).toWeb();
|
||||
} else {
|
||||
// Create web ReadableStream from Node.js stream
|
||||
this.options.requestBody = new ReadableStream({
|
||||
async start(controller) {
|
||||
nodeStream.on('data', (chunk: any) => {
|
||||
controller.enqueue(new Uint8Array(chunk));
|
||||
});
|
||||
nodeStream.on('end', () => {
|
||||
controller.close();
|
||||
});
|
||||
nodeStream.on('error', (err: any) => {
|
||||
controller.error(err);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Warn if raw streaming function is provided (not supported in Bun)
|
||||
if (requestDataFunc && (options as any).__rawStreamFunc) {
|
||||
throw new Error(
|
||||
'Raw streaming with .raw() is not supported in Bun. Use .stream() with web ReadableStream instead.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the full URL with query parameters
|
||||
*/
|
||||
private buildUrl(): string {
|
||||
// For unix sockets, we need to extract the HTTP path part
|
||||
if (CoreRequest.isUnixSocket(this.url)) {
|
||||
const { path } = CoreRequest.parseUnixSocketUrl(this.url);
|
||||
|
||||
// Build URL for the HTTP request (the hostname doesn't matter for unix sockets)
|
||||
if (
|
||||
!this.options.queryParams ||
|
||||
Object.keys(this.options.queryParams).length === 0
|
||||
) {
|
||||
return `http://localhost${path}`;
|
||||
}
|
||||
|
||||
const url = new URL(`http://localhost${path}`);
|
||||
Object.entries(this.options.queryParams).forEach(([key, value]) => {
|
||||
url.searchParams.append(key, value);
|
||||
});
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
// Regular HTTP/HTTPS URL
|
||||
if (
|
||||
!this.options.queryParams ||
|
||||
Object.keys(this.options.queryParams).length === 0
|
||||
) {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
const url = new URL(this.url);
|
||||
Object.entries(this.options.queryParams).forEach(([key, value]) => {
|
||||
url.searchParams.append(key, value);
|
||||
});
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert our options to fetch RequestInit with Bun-specific extensions
|
||||
*/
|
||||
private buildFetchOptions(): RequestInit & { unix?: string } {
|
||||
const fetchOptions: RequestInit & { unix?: string } = {
|
||||
method: this.options.method,
|
||||
headers: this.options.headers,
|
||||
credentials: this.options.credentials,
|
||||
mode: this.options.mode,
|
||||
cache: this.options.cache,
|
||||
redirect: this.options.redirect,
|
||||
referrer: this.options.referrer,
|
||||
referrerPolicy: this.options.referrerPolicy,
|
||||
integrity: this.options.integrity,
|
||||
keepalive: this.options.keepAlive,
|
||||
signal: this.options.signal,
|
||||
};
|
||||
|
||||
// Handle unix socket
|
||||
if (CoreRequest.isUnixSocket(this.url)) {
|
||||
const { socketPath } = CoreRequest.parseUnixSocketUrl(this.url);
|
||||
fetchOptions.unix = socketPath;
|
||||
} else if (this.options.unix) {
|
||||
// Direct unix option was provided
|
||||
fetchOptions.unix = this.options.unix;
|
||||
} else if (this.options.socketPath) {
|
||||
// Legacy Node.js socketPath option - convert to Bun's unix option
|
||||
fetchOptions.unix = this.options.socketPath;
|
||||
}
|
||||
|
||||
// Handle request body
|
||||
if (this.options.requestBody !== undefined) {
|
||||
if (
|
||||
typeof this.options.requestBody === 'string' ||
|
||||
this.options.requestBody instanceof ArrayBuffer ||
|
||||
this.options.requestBody instanceof Uint8Array ||
|
||||
this.options.requestBody instanceof FormData ||
|
||||
this.options.requestBody instanceof URLSearchParams ||
|
||||
this.options.requestBody instanceof ReadableStream ||
|
||||
// Check for Buffer (Bun supports Node.js Buffer)
|
||||
(typeof Buffer !== 'undefined' && this.options.requestBody instanceof Buffer)
|
||||
) {
|
||||
fetchOptions.body = this.options.requestBody as BodyInit;
|
||||
|
||||
// If streaming, we need to set duplex mode
|
||||
if (this.options.requestBody instanceof ReadableStream) {
|
||||
(fetchOptions as any).duplex = 'half';
|
||||
}
|
||||
} else {
|
||||
// Convert objects to JSON
|
||||
fetchOptions.body = JSON.stringify(this.options.requestBody);
|
||||
// Set content-type if not already set
|
||||
if (!fetchOptions.headers) {
|
||||
fetchOptions.headers = { 'Content-Type': 'application/json' };
|
||||
} else if (fetchOptions.headers instanceof Headers) {
|
||||
if (!fetchOptions.headers.has('Content-Type')) {
|
||||
fetchOptions.headers.set('Content-Type', 'application/json');
|
||||
}
|
||||
} else if (
|
||||
typeof fetchOptions.headers === 'object' &&
|
||||
!Array.isArray(fetchOptions.headers)
|
||||
) {
|
||||
const headersObj = fetchOptions.headers as Record<string, string>;
|
||||
if (!headersObj['Content-Type']) {
|
||||
headersObj['Content-Type'] = 'application/json';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle timeout
|
||||
if (this.options.timeout || this.options.hardDataCuttingTimeout) {
|
||||
const timeout =
|
||||
this.options.hardDataCuttingTimeout || this.options.timeout;
|
||||
this.abortController = new AbortController();
|
||||
this.timeoutId = setTimeout(() => {
|
||||
if (this.abortController) {
|
||||
this.abortController.abort();
|
||||
}
|
||||
}, timeout);
|
||||
fetchOptions.signal = this.abortController.signal;
|
||||
}
|
||||
|
||||
return fetchOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire the request and return a CoreResponse
|
||||
*/
|
||||
async fire(): Promise<CoreResponse> {
|
||||
const response = await this.fireCore();
|
||||
return new CoreResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire the request and return the raw Response
|
||||
*/
|
||||
async fireCore(): Promise<Response> {
|
||||
const url = this.buildUrl();
|
||||
const options = this.buildFetchOptions();
|
||||
|
||||
try {
|
||||
const response = await fetch(url, options);
|
||||
// Clear timeout on successful response
|
||||
this.clearTimeout();
|
||||
return response;
|
||||
} catch (error) {
|
||||
// Clear timeout on error
|
||||
this.clearTimeout();
|
||||
if (error.name === 'AbortError') {
|
||||
throw new Error('Request timed out');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the timeout and abort controller
|
||||
*/
|
||||
private clearTimeout(): void {
|
||||
if (this.timeoutId) {
|
||||
clearTimeout(this.timeoutId);
|
||||
this.timeoutId = null;
|
||||
}
|
||||
if (this.abortController) {
|
||||
this.abortController = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Static factory method to create and fire a request
|
||||
*/
|
||||
static async create(
|
||||
url: string,
|
||||
options: types.IBunRequestOptions = {},
|
||||
): Promise<CoreResponse> {
|
||||
const request = new CoreRequest(url, options);
|
||||
return request.fire();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience exports for backward compatibility
|
||||
*/
|
||||
export const isUnixSocket = CoreRequest.isUnixSocket;
|
||||
export const parseUnixSocketUrl = CoreRequest.parseUnixSocketUrl;
|
||||
81
ts/core_bun/response.ts
Normal file
81
ts/core_bun/response.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import * as types from './types.js';
|
||||
import { CoreResponse as AbstractCoreResponse } from '../core_base/response.js';
|
||||
|
||||
/**
|
||||
* Bun implementation of Core Response class that wraps native fetch Response
|
||||
*/
|
||||
export class CoreResponse<T = any>
|
||||
extends AbstractCoreResponse<T>
|
||||
implements types.IBunResponse<T>
|
||||
{
|
||||
private response: Response;
|
||||
private responseClone: Response;
|
||||
|
||||
// Public properties
|
||||
public readonly ok: boolean;
|
||||
public readonly status: number;
|
||||
public readonly statusText: string;
|
||||
public readonly headers: types.Headers;
|
||||
public readonly url: string;
|
||||
|
||||
constructor(response: Response) {
|
||||
super();
|
||||
// Clone the response so we can read the body multiple times if needed
|
||||
this.response = response;
|
||||
this.responseClone = response.clone();
|
||||
|
||||
this.ok = response.ok;
|
||||
this.status = response.status;
|
||||
this.statusText = response.statusText;
|
||||
this.url = response.url;
|
||||
|
||||
// Convert Headers to plain object
|
||||
this.headers = {};
|
||||
response.headers.forEach((value, key) => {
|
||||
this.headers[key] = value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse response as JSON
|
||||
*/
|
||||
async json(): Promise<T> {
|
||||
this.ensureNotConsumed();
|
||||
try {
|
||||
return await this.response.json();
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse JSON: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get response as text
|
||||
*/
|
||||
async text(): Promise<string> {
|
||||
this.ensureNotConsumed();
|
||||
return await this.response.text();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get response as ArrayBuffer
|
||||
*/
|
||||
async arrayBuffer(): Promise<ArrayBuffer> {
|
||||
this.ensureNotConsumed();
|
||||
return await this.response.arrayBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get response as a readable stream (Web Streams API)
|
||||
*/
|
||||
stream(): ReadableStream<Uint8Array> | null {
|
||||
this.ensureNotConsumed();
|
||||
return this.response.body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw Response object
|
||||
*/
|
||||
raw(): Response {
|
||||
return this.responseClone;
|
||||
}
|
||||
}
|
||||
23
ts/core_bun/types.ts
Normal file
23
ts/core_bun/types.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import * as baseTypes from '../core_base/types.js';
|
||||
|
||||
// Re-export base types
|
||||
export * from '../core_base/types.js';
|
||||
|
||||
/**
|
||||
* Bun-specific request options
|
||||
*/
|
||||
export interface IBunRequestOptions extends baseTypes.ICoreRequestOptions {
|
||||
/**
|
||||
* Unix domain socket path for Bun's fetch
|
||||
* When provided, the request will be sent over the unix socket instead of TCP
|
||||
*/
|
||||
unix?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bun-specific response extensions
|
||||
*/
|
||||
export interface IBunResponse<T = any> extends baseTypes.ICoreResponse<T> {
|
||||
// Access to raw Response object
|
||||
raw(): Response;
|
||||
}
|
||||
23
ts/core_deno/deno.types.ts
Normal file
23
ts/core_deno/deno.types.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Minimal Deno type definitions for compilation in Node.js environment
|
||||
* These types are only used during build-time type checking
|
||||
* At runtime, actual Deno APIs will be available in Deno environment
|
||||
*/
|
||||
|
||||
declare global {
|
||||
namespace Deno {
|
||||
interface HttpClient {
|
||||
close(): void;
|
||||
}
|
||||
|
||||
interface CreateHttpClientOptions {
|
||||
proxy?: {
|
||||
url: string;
|
||||
};
|
||||
}
|
||||
|
||||
function createHttpClient(options: CreateHttpClientOptions): HttpClient;
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
3
ts/core_deno/index.ts
Normal file
3
ts/core_deno/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
// Core Deno exports - Deno's native fetch implementation with unix socket support via HttpClient
|
||||
export * from './response.js';
|
||||
export { CoreRequest } from './request.js';
|
||||
295
ts/core_deno/request.ts
Normal file
295
ts/core_deno/request.ts
Normal file
@@ -0,0 +1,295 @@
|
||||
/// <reference path="./deno.types.ts" />
|
||||
import * as types from './types.js';
|
||||
import { CoreResponse } from './response.js';
|
||||
import { CoreRequest as AbstractCoreRequest } from '../core_base/request.js';
|
||||
|
||||
/**
|
||||
* Cache for HttpClient instances keyed by socket path
|
||||
* This prevents creating multiple clients for the same socket
|
||||
*/
|
||||
const httpClientCache = new Map<string, Deno.HttpClient>();
|
||||
|
||||
/**
|
||||
* Deno implementation of Core Request class using native fetch with unix socket support via HttpClient
|
||||
*/
|
||||
export class CoreRequest extends AbstractCoreRequest<
|
||||
types.IDenoRequestOptions,
|
||||
CoreResponse
|
||||
> {
|
||||
private timeoutId: ReturnType<typeof setTimeout> | null = null;
|
||||
private abortController: AbortController | null = null;
|
||||
private createdClient: Deno.HttpClient | null = null;
|
||||
private requestDataFunc: ((req: any) => void) | null;
|
||||
|
||||
constructor(
|
||||
url: string,
|
||||
options: types.IDenoRequestOptions = {},
|
||||
requestDataFunc: ((req: any) => void) | null = null,
|
||||
) {
|
||||
super(url, options);
|
||||
this.requestDataFunc = requestDataFunc;
|
||||
|
||||
// Check for unsupported Node.js-specific options
|
||||
if (options.agent) {
|
||||
throw new Error(
|
||||
'Node.js specific option (agent) is not supported in Deno implementation',
|
||||
);
|
||||
}
|
||||
|
||||
// Handle Node.js stream conversion if requestDataFunc is provided
|
||||
if (requestDataFunc && (options as any).__nodeStream) {
|
||||
// Convert Node.js stream to web ReadableStream for Deno
|
||||
const nodeStream = (options as any).__nodeStream;
|
||||
|
||||
// Create web ReadableStream from Node.js stream
|
||||
this.options.requestBody = new ReadableStream({
|
||||
async start(controller) {
|
||||
nodeStream.on('data', (chunk: any) => {
|
||||
controller.enqueue(new Uint8Array(chunk));
|
||||
});
|
||||
nodeStream.on('end', () => {
|
||||
controller.close();
|
||||
});
|
||||
nodeStream.on('error', (err: any) => {
|
||||
controller.error(err);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Throw error if raw streaming function is provided (not supported in Deno)
|
||||
if (requestDataFunc && (options as any).__rawStreamFunc) {
|
||||
throw new Error(
|
||||
'Raw streaming with .raw() is not supported in Deno. Use .stream() with web ReadableStream instead.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create an HttpClient for unix socket communication
|
||||
*/
|
||||
private getHttpClient(): Deno.HttpClient | undefined {
|
||||
// If client was explicitly provided, use it
|
||||
if (this.options.client) {
|
||||
return this.options.client;
|
||||
}
|
||||
|
||||
// Check if we need a unix socket client
|
||||
const socketPath = this.options.socketPath ||
|
||||
(CoreRequest.isUnixSocket(this.url)
|
||||
? CoreRequest.parseUnixSocketUrl(this.url).socketPath
|
||||
: null);
|
||||
|
||||
if (!socketPath) {
|
||||
return undefined; // Use default client
|
||||
}
|
||||
|
||||
// Check cache first
|
||||
if (httpClientCache.has(socketPath)) {
|
||||
return httpClientCache.get(socketPath);
|
||||
}
|
||||
|
||||
// Create new HttpClient for this socket
|
||||
const client = Deno.createHttpClient({
|
||||
proxy: {
|
||||
url: `unix://${socketPath}`,
|
||||
},
|
||||
});
|
||||
|
||||
// Cache it
|
||||
httpClientCache.set(socketPath, client);
|
||||
this.createdClient = client;
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the full URL with query parameters
|
||||
*/
|
||||
private buildUrl(): string {
|
||||
// For unix sockets, we need to extract the HTTP path part
|
||||
if (CoreRequest.isUnixSocket(this.url)) {
|
||||
const { path } = CoreRequest.parseUnixSocketUrl(this.url);
|
||||
|
||||
// Build URL for the HTTP request (the hostname doesn't matter for unix sockets)
|
||||
if (
|
||||
!this.options.queryParams ||
|
||||
Object.keys(this.options.queryParams).length === 0
|
||||
) {
|
||||
return `http://localhost${path}`;
|
||||
}
|
||||
|
||||
const url = new URL(`http://localhost${path}`);
|
||||
Object.entries(this.options.queryParams).forEach(([key, value]) => {
|
||||
url.searchParams.append(key, value);
|
||||
});
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
// Regular HTTP/HTTPS URL
|
||||
if (
|
||||
!this.options.queryParams ||
|
||||
Object.keys(this.options.queryParams).length === 0
|
||||
) {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
const url = new URL(this.url);
|
||||
Object.entries(this.options.queryParams).forEach(([key, value]) => {
|
||||
url.searchParams.append(key, value);
|
||||
});
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert our options to fetch RequestInit
|
||||
*/
|
||||
private buildFetchOptions(): RequestInit & { client?: Deno.HttpClient } {
|
||||
const fetchOptions: RequestInit & { client?: Deno.HttpClient } = {
|
||||
method: this.options.method,
|
||||
headers: this.options.headers,
|
||||
credentials: this.options.credentials,
|
||||
mode: this.options.mode,
|
||||
cache: this.options.cache,
|
||||
redirect: this.options.redirect,
|
||||
referrer: this.options.referrer,
|
||||
referrerPolicy: this.options.referrerPolicy,
|
||||
integrity: this.options.integrity,
|
||||
keepalive: this.options.keepAlive,
|
||||
signal: this.options.signal,
|
||||
};
|
||||
|
||||
// Set the HttpClient (for unix sockets or custom configurations)
|
||||
const client = this.getHttpClient();
|
||||
if (client) {
|
||||
fetchOptions.client = client;
|
||||
}
|
||||
|
||||
// Handle request body
|
||||
if (this.options.requestBody !== undefined) {
|
||||
if (
|
||||
typeof this.options.requestBody === 'string' ||
|
||||
this.options.requestBody instanceof ArrayBuffer ||
|
||||
this.options.requestBody instanceof Uint8Array ||
|
||||
this.options.requestBody instanceof FormData ||
|
||||
this.options.requestBody instanceof URLSearchParams ||
|
||||
this.options.requestBody instanceof ReadableStream ||
|
||||
// Check for Buffer (Deno provides Buffer via Node.js compatibility)
|
||||
(typeof Buffer !== 'undefined' && this.options.requestBody instanceof Buffer)
|
||||
) {
|
||||
fetchOptions.body = this.options.requestBody as BodyInit;
|
||||
|
||||
// If streaming, we need to set duplex mode
|
||||
if (this.options.requestBody instanceof ReadableStream) {
|
||||
(fetchOptions as any).duplex = 'half';
|
||||
}
|
||||
} else {
|
||||
// Convert objects to JSON
|
||||
fetchOptions.body = JSON.stringify(this.options.requestBody);
|
||||
// Set content-type if not already set
|
||||
if (!fetchOptions.headers) {
|
||||
fetchOptions.headers = { 'Content-Type': 'application/json' };
|
||||
} else if (fetchOptions.headers instanceof Headers) {
|
||||
if (!fetchOptions.headers.has('Content-Type')) {
|
||||
fetchOptions.headers.set('Content-Type', 'application/json');
|
||||
}
|
||||
} else if (
|
||||
typeof fetchOptions.headers === 'object' &&
|
||||
!Array.isArray(fetchOptions.headers)
|
||||
) {
|
||||
const headersObj = fetchOptions.headers as Record<string, string>;
|
||||
if (!headersObj['Content-Type']) {
|
||||
headersObj['Content-Type'] = 'application/json';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle timeout
|
||||
if (this.options.timeout || this.options.hardDataCuttingTimeout) {
|
||||
const timeout =
|
||||
this.options.hardDataCuttingTimeout || this.options.timeout;
|
||||
this.abortController = new AbortController();
|
||||
this.timeoutId = setTimeout(() => {
|
||||
if (this.abortController) {
|
||||
this.abortController.abort();
|
||||
}
|
||||
}, timeout);
|
||||
fetchOptions.signal = this.abortController.signal;
|
||||
}
|
||||
|
||||
return fetchOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire the request and return a CoreResponse
|
||||
*/
|
||||
async fire(): Promise<CoreResponse> {
|
||||
const response = await this.fireCore();
|
||||
return new CoreResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire the request and return the raw Response
|
||||
*/
|
||||
async fireCore(): Promise<Response> {
|
||||
const url = this.buildUrl();
|
||||
const options = this.buildFetchOptions();
|
||||
|
||||
try {
|
||||
const response = await fetch(url, options);
|
||||
// Clear timeout on successful response
|
||||
this.clearTimeout();
|
||||
return response;
|
||||
} catch (error) {
|
||||
// Clear timeout on error
|
||||
this.clearTimeout();
|
||||
if (error.name === 'AbortError') {
|
||||
throw new Error('Request timed out');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the timeout and abort controller
|
||||
* Note: We don't close the HttpClient here as it's cached for reuse
|
||||
*/
|
||||
private clearTimeout(): void {
|
||||
if (this.timeoutId) {
|
||||
clearTimeout(this.timeoutId);
|
||||
this.timeoutId = null;
|
||||
}
|
||||
if (this.abortController) {
|
||||
this.abortController = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Static factory method to create and fire a request
|
||||
*/
|
||||
static async create(
|
||||
url: string,
|
||||
options: types.IDenoRequestOptions = {},
|
||||
): Promise<CoreResponse> {
|
||||
const request = new CoreRequest(url, options);
|
||||
return request.fire();
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to clear the HttpClient cache
|
||||
* Call this when you want to force new clients to be created
|
||||
*/
|
||||
static clearClientCache(): void {
|
||||
httpClientCache.forEach((client) => {
|
||||
client.close();
|
||||
});
|
||||
httpClientCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience exports for backward compatibility
|
||||
*/
|
||||
export const isUnixSocket = CoreRequest.isUnixSocket;
|
||||
export const parseUnixSocketUrl = CoreRequest.parseUnixSocketUrl;
|
||||
81
ts/core_deno/response.ts
Normal file
81
ts/core_deno/response.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import * as types from './types.js';
|
||||
import { CoreResponse as AbstractCoreResponse } from '../core_base/response.js';
|
||||
|
||||
/**
|
||||
* Deno implementation of Core Response class that wraps native fetch Response
|
||||
*/
|
||||
export class CoreResponse<T = any>
|
||||
extends AbstractCoreResponse<T>
|
||||
implements types.IDenoResponse<T>
|
||||
{
|
||||
private response: Response;
|
||||
private responseClone: Response;
|
||||
|
||||
// Public properties
|
||||
public readonly ok: boolean;
|
||||
public readonly status: number;
|
||||
public readonly statusText: string;
|
||||
public readonly headers: types.Headers;
|
||||
public readonly url: string;
|
||||
|
||||
constructor(response: Response) {
|
||||
super();
|
||||
// Clone the response so we can read the body multiple times if needed
|
||||
this.response = response;
|
||||
this.responseClone = response.clone();
|
||||
|
||||
this.ok = response.ok;
|
||||
this.status = response.status;
|
||||
this.statusText = response.statusText;
|
||||
this.url = response.url;
|
||||
|
||||
// Convert Headers to plain object
|
||||
this.headers = {};
|
||||
response.headers.forEach((value, key) => {
|
||||
this.headers[key] = value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse response as JSON
|
||||
*/
|
||||
async json(): Promise<T> {
|
||||
this.ensureNotConsumed();
|
||||
try {
|
||||
return await this.response.json();
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse JSON: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get response as text
|
||||
*/
|
||||
async text(): Promise<string> {
|
||||
this.ensureNotConsumed();
|
||||
return await this.response.text();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get response as ArrayBuffer
|
||||
*/
|
||||
async arrayBuffer(): Promise<ArrayBuffer> {
|
||||
this.ensureNotConsumed();
|
||||
return await this.response.arrayBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get response as a readable stream (Web Streams API)
|
||||
*/
|
||||
stream(): ReadableStream<Uint8Array> | null {
|
||||
this.ensureNotConsumed();
|
||||
return this.response.body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw Response object
|
||||
*/
|
||||
raw(): Response {
|
||||
return this.responseClone;
|
||||
}
|
||||
}
|
||||
24
ts/core_deno/types.ts
Normal file
24
ts/core_deno/types.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/// <reference path="./deno.types.ts" />
|
||||
import * as baseTypes from '../core_base/types.js';
|
||||
|
||||
// Re-export base types
|
||||
export * from '../core_base/types.js';
|
||||
|
||||
/**
|
||||
* Deno-specific request options
|
||||
*/
|
||||
export interface IDenoRequestOptions extends baseTypes.ICoreRequestOptions {
|
||||
/**
|
||||
* Deno HttpClient instance for custom configurations including unix sockets
|
||||
* If not provided and socketPath is specified, a client will be created automatically
|
||||
*/
|
||||
client?: Deno.HttpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deno-specific response extensions
|
||||
*/
|
||||
export interface IDenoResponse<T = any> extends baseTypes.ICoreResponse<T> {
|
||||
// Access to raw Response object
|
||||
raw(): Response;
|
||||
}
|
||||
@@ -72,15 +72,6 @@ export class CoreResponse<T = any>
|
||||
return this.response.body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Node.js stream method - not available in browser
|
||||
*/
|
||||
streamNode(): never {
|
||||
throw new Error(
|
||||
'streamNode() is not available in browser/fetch environment. Use stream() for web-style ReadableStream.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw Response object
|
||||
*/
|
||||
|
||||
@@ -3,8 +3,9 @@ import * as fs from 'fs';
|
||||
import * as http from 'http';
|
||||
import * as https from 'https';
|
||||
import * as path from 'path';
|
||||
import * as stream from 'stream';
|
||||
|
||||
export { http, https, fs, path };
|
||||
export { http, https, fs, path, stream };
|
||||
|
||||
// pushrocks scope
|
||||
import * as smartpromise from '@push.rocks/smartpromise';
|
||||
|
||||
@@ -86,9 +86,7 @@ export class CoreRequest extends AbstractCoreRequest<
|
||||
|
||||
// Handle unix socket URLs
|
||||
if (CoreRequest.isUnixSocket(this.url)) {
|
||||
const { socketPath, path } = CoreRequest.parseUnixSocketUrl(
|
||||
this.options.path,
|
||||
);
|
||||
const { socketPath, path } = CoreRequest.parseUnixSocketUrl(this.url);
|
||||
this.options.socketPath = socketPath;
|
||||
this.options.path = path;
|
||||
}
|
||||
@@ -147,6 +145,12 @@ export class CoreRequest extends AbstractCoreRequest<
|
||||
this.options.requestBody.pipe(request).on('finish', () => {
|
||||
request.end();
|
||||
});
|
||||
} else if (this.options.requestBody instanceof ReadableStream) {
|
||||
// Convert web ReadableStream to Node.js Readable stream
|
||||
const nodeStream = plugins.stream.Readable.fromWeb(this.options.requestBody as any);
|
||||
nodeStream.pipe(request).on('finish', () => {
|
||||
request.end();
|
||||
});
|
||||
} else {
|
||||
// Write body as-is - caller is responsible for serialization
|
||||
const bodyData =
|
||||
|
||||
@@ -129,41 +129,15 @@ export class CoreResponse<T = any>
|
||||
stream(): ReadableStream<Uint8Array> | null {
|
||||
this.ensureNotConsumed();
|
||||
|
||||
// Convert Node.js stream to web stream
|
||||
// In Node.js 16.5+ we can use Readable.toWeb()
|
||||
// Convert Node.js stream to web stream using Readable.toWeb()
|
||||
// This creates a proper Node.js-compatible web stream that works with Readable.fromWeb()
|
||||
if (this.incomingMessage.readableEnded || this.incomingMessage.destroyed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a web ReadableStream from the Node.js stream
|
||||
const nodeStream = this.incomingMessage;
|
||||
return new ReadableStream<Uint8Array>({
|
||||
start(controller) {
|
||||
nodeStream.on('data', (chunk) => {
|
||||
controller.enqueue(new Uint8Array(chunk));
|
||||
});
|
||||
|
||||
nodeStream.on('end', () => {
|
||||
controller.close();
|
||||
});
|
||||
|
||||
nodeStream.on('error', (err) => {
|
||||
controller.error(err);
|
||||
});
|
||||
},
|
||||
|
||||
cancel() {
|
||||
nodeStream.destroy();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get response as a Node.js readable stream
|
||||
*/
|
||||
streamNode(): NodeJS.ReadableStream {
|
||||
this.ensureNotConsumed();
|
||||
return this.incomingMessage;
|
||||
// Use Readable.toWeb() to convert Node.js stream to web stream (Node.js 16.5+)
|
||||
// The returned type is automatically Node.js ReadableStream which is compatible with Readable.fromWeb()
|
||||
return plugins.stream.Readable.toWeb(this.incomingMessage) as any;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user