From c2a2a1b49bc93b48eaab957f2624253ce1b598f2 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 26 Mar 2025 12:21:51 +0100 Subject: [PATCH] Add Node JS support --- README.md | 59 ++- examples/download/esbuild.js | 18 +- examples/download/index.js | 13 +- examples/download/package-lock.json | 42 +- examples/download/package.json | 5 +- examples/upload-browser/.gitignore | 1 + examples/upload-browser/README.md | 23 + examples/upload-browser/esbuild.js | 22 + examples/upload-browser/index.html | 4 + examples/upload-browser/index.js | 36 ++ examples/upload-browser/package-lock.json | 531 ++++++++++++++++++++++ examples/upload-browser/package.json | 18 + examples/upload-node/README.md | 17 + examples/upload-node/index.js | 21 + examples/upload-node/package-lock.json | 64 +++ examples/upload-node/package.json | 17 + package-lock.json | 155 ++++--- package.json | 36 +- src/browser.ts | 1 + src/data/browser-download.ts | 74 +++ src/data/data.ts | 105 +---- src/data/node-download.ts | 69 +++ src/data/types.ts | 13 +- src/node.ts | 1 + 24 files changed, 1128 insertions(+), 217 deletions(-) create mode 100644 examples/upload-browser/.gitignore create mode 100644 examples/upload-browser/README.md create mode 100644 examples/upload-browser/esbuild.js create mode 100644 examples/upload-browser/index.html create mode 100644 examples/upload-browser/index.js create mode 100644 examples/upload-browser/package-lock.json create mode 100644 examples/upload-browser/package.json create mode 100644 examples/upload-node/README.md create mode 100644 examples/upload-node/index.js create mode 100644 examples/upload-node/package-lock.json create mode 100644 examples/upload-node/package.json create mode 100644 src/browser.ts create mode 100644 src/data/browser-download.ts create mode 100644 src/data/node-download.ts create mode 100644 src/node.ts diff --git a/README.md b/README.md index 05d7d1c..700f42d 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@ The SDK has a small bundle size and support tree shaking. The SDK is currently under early development and the API can change at any time. +## Breaking changes + +- Version 0.1.0 introduce download strategy to support browser and Node JS. + ## How to use ### Sync api @@ -272,24 +276,55 @@ const space = await data.space(); Upload a file in a streaming manner -- file (File, required) -- onProgress (onProgress: (loaded: number, total: number) => void, optional) -- metadata ({ filename?: string, mimetype?: string }, optional) +## Browser + +- stategy [BrowserDownloadStategy](./src/data/browser-download.ts#L5) - returns [UploadResponse](./src/data/types.ts#L80) Example: ```js -// Get file from previous event -const [file] = e.target.files -const metadata = { - filename: file.name, - mimetype: file.type, +const file = new File(["foo"], "foo.txt", { type: "text/plain" }); + +const onProgress = (loaded, total) => { + console.info("Loaded", loaded, "total", total); +}; + +const metadata = { filename: "foo.xt", mimetype: "text/plain" }; + +const stategy = new BrowserDownloadStategy(file, onProgress, metadata); + +const uploadResponse = data.upload(stategy); + +const res = await uploadResponse.result; + +if (res.error) { + console.error(res.data); + return; } -const upload = data.upload(file, (loaded: number, total: number) => { - // Use loaded and total so update a progress bar for example -}, metadata); -await upload.result(); + +console.info("CID is", res.data); +``` + +## Node + +- stategy [NodeDownloadStategy](./src/data/node-download.ts#L8) +- returns [UploadResponse](./src/data/types.ts#L80) + +Example: + +```js +const stategy = new NodeDownloadStategy("Hello World !"); +const uploadResponse = data.upload(stategy); + +const res = await uploadResponse.result; + +if (res.error) { + console.error(res.data); + return; +} + +console.info("CID is", res.data); ``` #### manifest diff --git a/examples/download/esbuild.js b/examples/download/esbuild.js index 28c297b..93c9831 100644 --- a/examples/download/esbuild.js +++ b/examples/download/esbuild.js @@ -1,19 +1,19 @@ -const { build } = require('esbuild') -const define = {} +const { build } = require("esbuild"); +const define = {}; for (const k in process.env) { - define[`process.env.${k}`] = JSON.stringify(process.env[k]) + define[`process.env.${k}`] = JSON.stringify(process.env[k]); } -if(!process.env["CODEX_NODE_URL"]) { - define[`process.env.CODEX_NODE_URL`] = "\"http://localhost:8080\"" +if (!process.env["CODEX_NODE_URL"]) { + define[`process.env.CODEX_NODE_URL`] = '"http://localhost:8080"'; } const options = { - entryPoints: ['./index.js'], - outfile: './index.bundle.js', + entryPoints: ["./index.js"], + outfile: "./index.bundle.js", bundle: true, define, -} +}; -build(options).catch(() => process.exit(1)) \ No newline at end of file +build(options).catch(() => process.exit(1)); diff --git a/examples/download/index.js b/examples/download/index.js index e445093..0432bc8 100644 --- a/examples/download/index.js +++ b/examples/download/index.js @@ -1,14 +1,15 @@ import { Codex } from "@codex-storage/sdk-js"; async function main() { - const codex = new Codex(process.env.CODEX_NODE_URL); + const codex = new Codex(process.env.CODEX_NODE_URL); - const data = codex.data + const data = codex.data; - const cid = process.env.CODEX_CID - const result = await data.networkDownloadStream(cid); + const cid = process.env.CODEX_CID; - console.info(await result.data.text()) + const result = await data.networkDownloadStream(cid); + + console.info(await result.data.text()); } -main() \ No newline at end of file +main(); diff --git a/examples/download/package-lock.json b/examples/download/package-lock.json index af9dc06..a7d1c79 100644 --- a/examples/download/package-lock.json +++ b/examples/download/package-lock.json @@ -1,31 +1,25 @@ { - "name": "download", + "name": "@codex-storage/sdk-js-download-example", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "download", + "name": "@codex-storage/sdk-js-download-example", "version": "1.0.0", "license": "ISC", "dependencies": { - "@codex-storage/sdk-js": "^0.0.23" + "@codex-storage/sdk-js": ".." }, "devDependencies": { - "esbuild": "^0.25.1" + "esbuild": "^0.25.1", + "prettier": "^3.5.3" } }, + "..": {}, "node_modules/@codex-storage/sdk-js": { - "version": "0.0.23", - "resolved": "https://registry.npmjs.org/@codex-storage/sdk-js/-/sdk-js-0.0.23.tgz", - "integrity": "sha512-+ktJs396GERPudRh5zjTvOMjwo3mRHVYN901Qvs0q3YlRK983aewSqJ+Z7NttSQ27oxTcvxQVrilcvzZRHQTkg==", - "license": "MIT", - "dependencies": { - "valibot": "^0.32.0" - }, - "engines": { - "node": ">=20" - } + "resolved": "..", + "link": true }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.1", @@ -493,11 +487,21 @@ "@esbuild/win32-x64": "0.25.1" } }, - "node_modules/valibot": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.32.0.tgz", - "integrity": "sha512-FXBnJl4bNOmeg7lQv+jfvo/wADsRBN8e9C3r+O77Re3dEnDma8opp7p4hcIbF7XJJ30h/5SVohdjer17/sHOsQ==", - "license": "MIT" + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } } } } diff --git a/examples/download/package.json b/examples/download/package.json index 4211911..6dcbd6b 100644 --- a/examples/download/package.json +++ b/examples/download/package.json @@ -9,9 +9,10 @@ "license": "ISC", "description": "", "dependencies": { - "@codex-storage/sdk-js": "^0.0.23" + "@codex-storage/sdk-js": ".." }, "devDependencies": { - "esbuild": "^0.25.1" + "esbuild": "^0.25.1", + "prettier": "^3.5.3" } } diff --git a/examples/upload-browser/.gitignore b/examples/upload-browser/.gitignore new file mode 100644 index 0000000..91097d8 --- /dev/null +++ b/examples/upload-browser/.gitignore @@ -0,0 +1 @@ +index.bundle.js \ No newline at end of file diff --git a/examples/upload-browser/README.md b/examples/upload-browser/README.md new file mode 100644 index 0000000..1c13969 --- /dev/null +++ b/examples/upload-browser/README.md @@ -0,0 +1,23 @@ +# Download example + +Small example to show how to download a file in the browser with Codex. + +## Install dependencies + +```bash +npm install +``` + +## Build the javascript asset + +```bash +CODEX_CID=REPLACE_BY_YOUR_CIDE npm run build +``` + +The response will be displayed as text, so it is better to test with .txt files. + +Note: You can define `CODEX_NODE_URL`, default value is "http://localhost:8080". + +## Check the results + +Open the index.html and open the web console. diff --git a/examples/upload-browser/esbuild.js b/examples/upload-browser/esbuild.js new file mode 100644 index 0000000..37b09c6 --- /dev/null +++ b/examples/upload-browser/esbuild.js @@ -0,0 +1,22 @@ +const { build } = require("esbuild"); +const define = {}; + +for (const k in process.env) { + define[`process.env.${k}`] = JSON.stringify(process.env[k]); +} + +if (!process.env["CODEX_NODE_URL"]) { + define[`process.env.CODEX_NODE_URL`] = '"http://localhost:8080"'; +} + +const options = { + entryPoints: ["./index.js"], + outfile: "./index.bundle.js", + bundle: true, + define, + logOverride: { + "ignored-bare-import": "silent", + }, +}; + +build(options).catch(() => process.exit(1)); diff --git a/examples/upload-browser/index.html b/examples/upload-browser/index.html new file mode 100644 index 0000000..0a494ed --- /dev/null +++ b/examples/upload-browser/index.html @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/examples/upload-browser/index.js b/examples/upload-browser/index.js new file mode 100644 index 0000000..c31b1c0 --- /dev/null +++ b/examples/upload-browser/index.js @@ -0,0 +1,36 @@ +import { Codex } from "@codex-storage/sdk-js"; +import { BrowserDownloadStategy } from "@codex-storage/sdk-js/browser"; + +async function main() { + const codex = new Codex(process.env.CODEX_NODE_URL); + + const data = codex.data; + + const file = new File(["foo"], "foo.txt", { + type: "text/plain", + }); + + const onProgress = (loaded, total) => { + console.info("Loaded", loaded, "total", total); + }; + + const metadata = { + filename: "foo.xt", + mimetype: "text/plain", + }; + + const stategy = new BrowserDownloadStategy(file, onProgress, metadata); + + const uploadResponse = data.upload(stategy); + + const res = await uploadResponse.result; + + if (res.error) { + console.error(res.data); + return; + } + + console.info("CID is", res.data); +} + +main(); diff --git a/examples/upload-browser/package-lock.json b/examples/upload-browser/package-lock.json new file mode 100644 index 0000000..88dca18 --- /dev/null +++ b/examples/upload-browser/package-lock.json @@ -0,0 +1,531 @@ +{ + "name": "@codex-storage/sdk-js-update-browser-example", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@codex-storage/sdk-js-update-browser-example", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@codex-storage/sdk-js": "../.." + }, + "devDependencies": { + "esbuild": "^0.25.1", + "prettier": "^3.5.3" + } + }, + "..": { + "extraneous": true + }, + "../..": { + "name": "@codex-storage/sdk-js", + "version": "0.0.23", + "license": "MIT", + "dependencies": { + "undici": "^7.5.0", + "valibot": "^0.32.0" + }, + "devDependencies": { + "@tsconfig/strictest": "^2.0.5", + "prettier": "^3.5.3", + "tsup": "^8.3.6", + "typescript": "^5.8.2", + "vitest": "^3.0.9" + }, + "engines": { + "node": ">=20.18.1" + } + }, + "../dist": { + "extraneous": true + }, + "node_modules/@codex-storage/sdk-js": { + "resolved": "../..", + "link": true + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz", + "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz", + "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz", + "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz", + "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz", + "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz", + "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz", + "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz", + "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz", + "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz", + "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz", + "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz", + "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz", + "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz", + "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz", + "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz", + "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz", + "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz", + "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz", + "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz", + "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz", + "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz", + "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz", + "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz", + "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz", + "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", + "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.1", + "@esbuild/android-arm": "0.25.1", + "@esbuild/android-arm64": "0.25.1", + "@esbuild/android-x64": "0.25.1", + "@esbuild/darwin-arm64": "0.25.1", + "@esbuild/darwin-x64": "0.25.1", + "@esbuild/freebsd-arm64": "0.25.1", + "@esbuild/freebsd-x64": "0.25.1", + "@esbuild/linux-arm": "0.25.1", + "@esbuild/linux-arm64": "0.25.1", + "@esbuild/linux-ia32": "0.25.1", + "@esbuild/linux-loong64": "0.25.1", + "@esbuild/linux-mips64el": "0.25.1", + "@esbuild/linux-ppc64": "0.25.1", + "@esbuild/linux-riscv64": "0.25.1", + "@esbuild/linux-s390x": "0.25.1", + "@esbuild/linux-x64": "0.25.1", + "@esbuild/netbsd-arm64": "0.25.1", + "@esbuild/netbsd-x64": "0.25.1", + "@esbuild/openbsd-arm64": "0.25.1", + "@esbuild/openbsd-x64": "0.25.1", + "@esbuild/sunos-x64": "0.25.1", + "@esbuild/win32-arm64": "0.25.1", + "@esbuild/win32-ia32": "0.25.1", + "@esbuild/win32-x64": "0.25.1" + } + }, + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + } + } +} diff --git a/examples/upload-browser/package.json b/examples/upload-browser/package.json new file mode 100644 index 0000000..60b3e29 --- /dev/null +++ b/examples/upload-browser/package.json @@ -0,0 +1,18 @@ +{ + "name": "@codex-storage/sdk-js-update-browser-example", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "build": "node esbuild.js" + }, + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "@codex-storage/sdk-js": "../.." + }, + "devDependencies": { + "esbuild": "^0.25.1", + "prettier": "^3.5.3" + } +} diff --git a/examples/upload-node/README.md b/examples/upload-node/README.md new file mode 100644 index 0000000..d1ebb90 --- /dev/null +++ b/examples/upload-node/README.md @@ -0,0 +1,17 @@ +# Download example + +Small example to show how to download a file in the browser with Codex. + +## Install dependencies + +```bash +npm install +``` + +## Run node + +```bash +node index.js +``` + +Note: You can define `CODEX_NODE_URL`, default value is "http://localhost:8080". diff --git a/examples/upload-node/index.js b/examples/upload-node/index.js new file mode 100644 index 0000000..d1498be --- /dev/null +++ b/examples/upload-node/index.js @@ -0,0 +1,21 @@ +const { Codex } = require("@codex-storage/sdk-js"); +const { NodeDownloadStategy } = require("@codex-storage/sdk-js/node"); + +async function main() { + const codex = new Codex(process.env.CODEX_NODE_URL || "http://localhost:8080"); + const data = codex.data + + const stategy = new NodeDownloadStategy("Hello World !") + const uploadResponse = data.upload(stategy); + + const res = await uploadResponse.result + + if (res.error) { + console.error(res.data) + return + } + + console.info("CID is", res.data) +} + +main() \ No newline at end of file diff --git a/examples/upload-node/package-lock.json b/examples/upload-node/package-lock.json new file mode 100644 index 0000000..65237cc --- /dev/null +++ b/examples/upload-node/package-lock.json @@ -0,0 +1,64 @@ +{ + "name": "@codex-storage/sdk-js-update-node-example", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@codex-storage/sdk-js-update-node-example", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@codex-storage/sdk-js": "../.." + }, + "devDependencies": { + "prettier": "^3.5.3" + } + }, + "..": { + "extraneous": true + }, + "../..": { + "name": "@codex-storage/sdk-js", + "version": "0.0.23", + "license": "MIT", + "dependencies": { + "undici": "^7.5.0", + "valibot": "^0.32.0" + }, + "devDependencies": { + "@tsconfig/strictest": "^2.0.5", + "prettier": "^3.5.3", + "tsup": "^8.3.6", + "typescript": "^5.8.2", + "vitest": "^3.0.9" + }, + "engines": { + "node": ">=20.18.1" + } + }, + "../dist": { + "extraneous": true + }, + "node_modules/@codex-storage/sdk-js": { + "resolved": "../..", + "link": true + }, + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + } + } +} diff --git a/examples/upload-node/package.json b/examples/upload-node/package.json new file mode 100644 index 0000000..0629a13 --- /dev/null +++ b/examples/upload-node/package.json @@ -0,0 +1,17 @@ +{ + "name": "@codex-storage/sdk-js-update-node-example", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "build": "node esbuild.js" + }, + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "@codex-storage/sdk-js": "../.." + }, + "devDependencies": { + "prettier": "^3.5.3" + } +} diff --git a/package-lock.json b/package-lock.json index dafefa5..80a8b10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,25 +1,26 @@ { "name": "@codex-storage/sdk-js", - "version": "0.0.23", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@codex-storage/sdk-js", - "version": "0.0.23", + "version": "0.1.0", "license": "MIT", "dependencies": { + "undici": "^7.5.0", "valibot": "^0.32.0" }, "devDependencies": { "@tsconfig/strictest": "^2.0.5", - "prettier": "^3.4.2", + "prettier": "^3.5.3", "tsup": "^8.3.6", - "typescript": "^5.7.3", - "vitest": "^3.0.5" + "typescript": "^5.8.2", + "vitest": "^3.0.9" }, "engines": { - "node": ">=20" + "node": ">=20.18.1" } }, "node_modules/@esbuild/aix-ppc64": { @@ -801,15 +802,15 @@ "dev": true }, "node_modules/@vitest/expect": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.5.tgz", - "integrity": "sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.9.tgz", + "integrity": "sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.0.5", - "@vitest/utils": "3.0.5", - "chai": "^5.1.2", + "@vitest/spy": "3.0.9", + "@vitest/utils": "3.0.9", + "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, "funding": { @@ -817,13 +818,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.5.tgz", - "integrity": "sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.9.tgz", + "integrity": "sha512-ryERPIBOnvevAkTq+L1lD+DTFBRcjueL9lOUfXsLfwP92h4e+Heb+PjiqS3/OURWPtywfafK0kj++yDFjWUmrA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.0.5", + "@vitest/spy": "3.0.9", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -844,9 +845,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz", - "integrity": "sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.9.tgz", + "integrity": "sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==", "dev": true, "license": "MIT", "dependencies": { @@ -857,38 +858,38 @@ } }, "node_modules/@vitest/runner": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.5.tgz", - "integrity": "sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.9.tgz", + "integrity": "sha512-NX9oUXgF9HPfJSwl8tUZCMP1oGx2+Sf+ru6d05QjzQz4OwWg0psEzwY6VexP2tTHWdOkhKHUIZH+fS6nA7jfOw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.0.5", - "pathe": "^2.0.2" + "@vitest/utils": "3.0.9", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.5.tgz", - "integrity": "sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.9.tgz", + "integrity": "sha512-AiLUiuZ0FuA+/8i19mTYd+re5jqjEc2jZbgJ2up0VY0Ddyyxg/uUtBDpIFAy4uzKaQxOW8gMgBdAJJ2ydhu39A==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.0.5", + "@vitest/pretty-format": "3.0.9", "magic-string": "^0.30.17", - "pathe": "^2.0.2" + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.5.tgz", - "integrity": "sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.9.tgz", + "integrity": "sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ==", "dev": true, "license": "MIT", "dependencies": { @@ -899,14 +900,14 @@ } }, "node_modules/@vitest/utils": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.5.tgz", - "integrity": "sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.9.tgz", + "integrity": "sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.0.5", - "loupe": "^3.1.2", + "@vitest/pretty-format": "3.0.9", + "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" }, "funding": { @@ -994,9 +995,9 @@ } }, "node_modules/chai": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", - "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", + "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", "dev": true, "license": "MIT", "dependencies": { @@ -1456,9 +1457,9 @@ } }, "node_modules/pathe": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.2.tgz", - "integrity": "sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, @@ -1572,9 +1573,9 @@ } }, "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", "bin": { @@ -2005,9 +2006,9 @@ } }, "node_modules/typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -2018,15 +2019,25 @@ "node": ">=14.17" } }, + "node_modules/undici": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.5.0.tgz", + "integrity": "sha512-NFQG741e8mJ0fLQk90xKxFdaSM7z4+IQpAgsFI36bCDY9Z2+aXXZjVy2uUksMouWfMI9+w5ejOq5zYYTBCQJDQ==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/valibot": { "version": "0.32.0", "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.32.0.tgz", - "integrity": "sha512-FXBnJl4bNOmeg7lQv+jfvo/wADsRBN8e9C3r+O77Re3dEnDma8opp7p4hcIbF7XJJ30h/5SVohdjer17/sHOsQ==" + "integrity": "sha512-FXBnJl4bNOmeg7lQv+jfvo/wADsRBN8e9C3r+O77Re3dEnDma8opp7p4hcIbF7XJJ30h/5SVohdjer17/sHOsQ==", + "license": "MIT" }, "node_modules/vite": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.0.tgz", - "integrity": "sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.3.tgz", + "integrity": "sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==", "dev": true, "license": "MIT", "dependencies": { @@ -2096,16 +2107,16 @@ } }, "node_modules/vite-node": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.5.tgz", - "integrity": "sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.9.tgz", + "integrity": "sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", "debug": "^4.4.0", "es-module-lexer": "^1.6.0", - "pathe": "^2.0.2", + "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0" }, "bin": { @@ -2119,31 +2130,31 @@ } }, "node_modules/vitest": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.5.tgz", - "integrity": "sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.9.tgz", + "integrity": "sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "3.0.5", - "@vitest/mocker": "3.0.5", - "@vitest/pretty-format": "^3.0.5", - "@vitest/runner": "3.0.5", - "@vitest/snapshot": "3.0.5", - "@vitest/spy": "3.0.5", - "@vitest/utils": "3.0.5", - "chai": "^5.1.2", + "@vitest/expect": "3.0.9", + "@vitest/mocker": "3.0.9", + "@vitest/pretty-format": "^3.0.9", + "@vitest/runner": "3.0.9", + "@vitest/snapshot": "3.0.9", + "@vitest/spy": "3.0.9", + "@vitest/utils": "3.0.9", + "chai": "^5.2.0", "debug": "^4.4.0", "expect-type": "^1.1.0", "magic-string": "^0.30.17", - "pathe": "^2.0.2", + "pathe": "^2.0.3", "std-env": "^3.8.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.0.5", + "vite-node": "3.0.9", "why-is-node-running": "^2.3.0" }, "bin": { @@ -2159,8 +2170,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.0.5", - "@vitest/ui": "3.0.5", + "@vitest/browser": "3.0.9", + "@vitest/ui": "3.0.9", "happy-dom": "*", "jsdom": "*" }, diff --git a/package.json b/package.json index 12592d9..4355d39 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@codex-storage/sdk-js", - "version": "0.0.23", + "version": "0.1.0", "description": "Codex SDK to interact with the Codex decentralized storage network.", "repository": { "type": "git", @@ -9,7 +9,7 @@ "scripts": { "prepack": "npm run build", "prebuild": "npm run compile && rm -Rf dist/*", - "build": "tsup src/index.ts src/async.ts --format esm,cjs --dts --sourcemap --treeshake", + "build": "tsup src/index.ts src/async.ts src/browser.ts src/node.ts --format esm,cjs --dts --sourcemap --treeshake", "compile": "tsc --noEmit", "test": "vitest run", "test:watch": "vitest", @@ -35,6 +35,26 @@ "default": "./dist/index.js" } }, + "./browser": { + "import": { + "types": "./dist/browser.d.ts", + "default": "./dist/browser.mjs" + }, + "require": { + "types": "./dist/browser.d.cts", + "default": "./dist/browser.js" + } + }, + "./node": { + "import": { + "types": "./dist/node.d.ts", + "default": "./dist/node.mjs" + }, + "require": { + "types": "./dist/node.d.cts", + "default": "./dist/node.js" + } + }, "./async": { "import": { "types": "./dist/async.d.ts", @@ -46,7 +66,6 @@ } } }, - "sideEffects": false, "files": [ "dist" ], @@ -54,16 +73,17 @@ "readme": "README.md", "license": "MIT", "engines": { - "node": ">=20" + "node": ">=20.18.1" }, "devDependencies": { "@tsconfig/strictest": "^2.0.5", - "prettier": "^3.4.2", + "prettier": "^3.5.3", "tsup": "^8.3.6", - "typescript": "^5.7.3", - "vitest": "^3.0.5" + "typescript": "^5.8.2", + "vitest": "^3.0.9" }, "dependencies": { + "undici": "^7.5.0", "valibot": "^0.32.0" } -} \ No newline at end of file +} diff --git a/src/browser.ts b/src/browser.ts new file mode 100644 index 0000000..05899f8 --- /dev/null +++ b/src/browser.ts @@ -0,0 +1 @@ +export * from "./data/browser-download"; diff --git a/src/data/browser-download.ts b/src/data/browser-download.ts new file mode 100644 index 0000000..1492135 --- /dev/null +++ b/src/data/browser-download.ts @@ -0,0 +1,74 @@ +import { CodexError } from "../errors/errors"; +import type { SafeValue } from "../values/values"; +import type { DownloadStategy } from "./types"; + +export class BrowserDownloadStategy implements DownloadStategy { + private readonly file: Document | XMLHttpRequestBodyInit; + private readonly onProgress: + | ((loaded: number, total: number) => void) + | undefined; + private readonly metadata: + | { filename?: string; mimetype?: string } + | undefined; + private xhr: XMLHttpRequest | undefined; + + constructor( + file: Document | XMLHttpRequestBodyInit, + onProgress?: (loaded: number, total: number) => void, + metadata?: { filename?: string; mimetype?: string } + ) { + this.file = file; + this.onProgress = onProgress; + this.metadata = metadata; + } + + download(url: string): Promise> { + const xhr = new XMLHttpRequest(); + this.xhr = xhr; + + return new Promise>((resolve) => { + xhr.upload.onprogress = (evt) => { + if (evt.lengthComputable) { + this.onProgress?.(evt.loaded, evt.total); + } + }; + + xhr.open("POST", url, true); + + if (this.metadata?.filename) { + xhr.setRequestHeader( + "Content-Disposition", + 'attachment; filename="' + this.metadata.filename + '"' + ); + } + + if (this.metadata?.mimetype) { + xhr.setRequestHeader("Content-Type", this.metadata.mimetype); + } + + xhr.send(this.file); + + xhr.onload = function () { + if (xhr.status != 200) { + resolve({ + error: true, + data: new CodexError(xhr.responseText, { code: xhr.status }), + }); + } else { + resolve({ error: false, data: xhr.response }); + } + }; + + xhr.onerror = function () { + resolve({ + error: true, + data: new CodexError("Something went wrong during the file upload."), + }); + }; + }); + } + + abort(): void { + this.xhr?.abort(); + } +} diff --git a/src/data/data.ts b/src/data/data.ts index a20baed..0ae23b0 100644 --- a/src/data/data.ts +++ b/src/data/data.ts @@ -1,11 +1,11 @@ import { Api } from "../api/config"; -import { CodexError } from "../errors/errors"; import { Fetch } from "../fetch-safe/fetch-safe"; import type { SafeValue } from "../values/values"; import type { CodexDataResponse, CodexManifest, CodexNodeSpace, + DownloadStategy, NetworkDownloadResponse, UploadResponse, } from "./types"; @@ -24,20 +24,15 @@ export class CodexData { cids(): Promise> { const url = this.url + Api.config.prefix + "/data"; - return Fetch.safeJson(url, { - method: "GET", - }).then((data) => { - if (data.error) { - return data; - } + return Fetch.safeJson(url, { method: "GET" }).then( + (data) => { + if (data.error) { + return data; + } - return { - error: false, - data: { - content: data.data.content, - }, - }; - }); + return { error: false, data: { content: data.data.content } }; + } + ); } /** @@ -46,9 +41,7 @@ export class CodexData { space() { const url = this.url + Api.config.prefix + "/space"; - return Fetch.safeJson(url, { - method: "GET", - }); + return Fetch.safeJson(url, { method: "GET" }); } /** @@ -57,59 +50,13 @@ export class CodexData { * XMLHttpRequest is used instead of fetch for this case, to obtain progress information. * A callback onProgress can be passed to receive upload progress data information. */ - upload( - file: Document | XMLHttpRequestBodyInit, - onProgress?: (loaded: number, total: number) => void, - metadata: { filename?: string, mimetype?: string } = {}, - ): UploadResponse { + upload(stategy: DownloadStategy): UploadResponse { const url = this.url + Api.config.prefix + "/data"; - const xhr = new XMLHttpRequest(); - - const promise = new Promise>((resolve) => { - xhr.upload.onprogress = (evt) => { - if (evt.lengthComputable) { - onProgress?.(evt.loaded, evt.total); - } - }; - - xhr.open("POST", url, true); - - if (metadata.filename) { - xhr.setRequestHeader("Content-Disposition", "attachment; filename=\"" + metadata.filename + "\"") - } - - if (metadata.mimetype) { - xhr.setRequestHeader("Content-Type", metadata.mimetype) - } - - xhr.send(file); - - xhr.onload = function () { - if (xhr.status != 200) { - resolve({ - error: true, - data: new CodexError(xhr.responseText, { - code: xhr.status, - }), - }); - } else { - resolve({ error: false, data: xhr.response }); - } - }; - - xhr.onerror = function () { - resolve({ - error: true, - data: new CodexError("Something went wrong during the file upload."), - }); - }; - }); - return { - result: promise, + result: stategy.download(url), abort: () => { - xhr.abort(); + stategy.abort(); }, }; } @@ -121,44 +68,38 @@ export class CodexData { async localDownload(cid: string): Promise> { const url = this.url + Api.config.prefix + "/data/" + cid; - return Fetch.safe(url, { - method: "GET", - }); + return Fetch.safe(url, { method: "GET" }); } /** * Download a file from the network in a streaming manner. * If the file is not available locally, it will be retrieved from other nodes in the network if able. */ - async networkDownload(cid: string): Promise> { + async networkDownload( + cid: string + ): Promise> { const url = this.url + Api.config.prefix + `/data/${cid}/network`; - return Fetch.safeJson(url, { - method: "POST" - }); + return Fetch.safeJson(url, { method: "POST" }); } /** - * Download a file from the network in a streaming manner. + * Download a file from the network in a streaming manner. * If the file is not available locally, it will be retrieved from other nodes in the network if able. */ async networkDownloadStream(cid: string): Promise> { const url = this.url + Api.config.prefix + `/data/${cid}/network/stream`; - return Fetch.safe(url, { - method: "GET" - }); + return Fetch.safe(url, { method: "GET" }); } /** - * Download only the dataset manifest from the network to the local node - * if it's not available locally. + * Download only the dataset manifest from the network to the local node + * if it's not available locally. */ async fetchManifest(cid: string) { const url = this.url + Api.config.prefix + `/data/${cid}/network/manifest`; - return Fetch.safeJson(url, { - method: "GET", - }); + return Fetch.safeJson(url, { method: "GET" }); } } diff --git a/src/data/node-download.ts b/src/data/node-download.ts new file mode 100644 index 0000000..3b75c54 --- /dev/null +++ b/src/data/node-download.ts @@ -0,0 +1,69 @@ +import type { Readable } from "node:stream"; +import { CodexError } from "../errors/errors"; +import type { SafeValue } from "../values/values"; +import Undici from "undici"; +import { type FormData } from "undici"; +import type { DownloadStategy } from "./types"; + +export class NodeDownloadStategy implements DownloadStategy { + private readonly body: + | string + | Buffer + | Uint8Array + | null + | Readable + | FormData; + private readonly metadata: + | { filename?: string; mimetype?: string } + | undefined; + private abortController: AbortController | undefined; + + constructor( + body: string | Buffer | Uint8Array | null | Readable | FormData, + metadata?: { filename?: string; mimetype?: string } + ) { + this.body = body; + this.metadata = metadata; + } + + async download(url: string): Promise> { + const headers: Record = {}; + + if (this.metadata?.filename) { + headers["Content-Disposition"] = + 'attachment; filename="' + this.metadata?.filename + '"'; + } + + if (this.metadata?.mimetype) { + headers["Content-Type"] = this.metadata?.mimetype; + } + + const controller = new AbortController(); + this.abortController = controller; + + const res = await Undici.request(url, { + method: "POST", + headers, + body: this.body, + signal: controller.signal, + }); + + if (res.statusCode < 200 || res.statusCode >= 300) { + const msg = `The status code is invalid got ${res.statusCode} - ${await res.body.text()} `; + return { + error: true, + data: new CodexError(msg, { code: res.statusCode }), + }; + } + + return { error: false, data: await res.body.text() }; + } + + abort(): void { + try { + this.abortController?.abort(); + } catch (_) { + // Nothing to do + } + } +} diff --git a/src/data/types.ts b/src/data/types.ts index a086e67..1b2b885 100644 --- a/src/data/types.ts +++ b/src/data/types.ts @@ -51,9 +51,7 @@ export type CodexDataContent = { manifest: CodexManifest; }; -export type CodexDataResponse = { - content: CodexDataContent[]; -}; +export type CodexDataResponse = { content: CodexDataContent[] }; export type CodexNodeSpace = { /** @@ -82,8 +80,9 @@ export type UploadResponse = { abort: () => void; }; +export type NetworkDownloadResponse = { cid: string; manifest: CodexManifest }; -export type NetworkDownloadResponse = { - cid: string - manifest: CodexManifest -} \ No newline at end of file +export interface DownloadStategy { + download(url: string): Promise>; + abort(): void; +} diff --git a/src/node.ts b/src/node.ts new file mode 100644 index 0000000..0db5593 --- /dev/null +++ b/src/node.ts @@ -0,0 +1 @@ +export * from "./data/node-download";