Add Node JS support

This commit is contained in:
Arnaud 2025-03-26 12:21:51 +01:00
parent f77978cd63
commit c2a2a1b49b
No known key found for this signature in database
GPG Key ID: 69D6CE281FCAE663
24 changed files with 1128 additions and 217 deletions

View File

@ -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

View File

@ -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))
build(options).catch(() => process.exit(1));

View File

@ -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()
main();

View File

@ -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"
}
}
}
}

View File

@ -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"
}
}

1
examples/upload-browser/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
index.bundle.js

View File

@ -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.

View File

@ -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));

View File

@ -0,0 +1,4 @@
<html>
<script src="./index.bundle.js">
</script>
</html>

View File

@ -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();

531
examples/upload-browser/package-lock.json generated Normal file
View File

@ -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"
}
}
}
}

View File

@ -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"
}
}

View File

@ -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".

View File

@ -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()

64
examples/upload-node/package-lock.json generated Normal file
View File

@ -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"
}
}
}
}

View File

@ -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"
}
}

155
package-lock.json generated
View File

@ -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": "*"
},

View File

@ -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"
}
}
}

1
src/browser.ts Normal file
View File

@ -0,0 +1 @@
export * from "./data/browser-download";

View File

@ -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<SafeValue<string>> {
const xhr = new XMLHttpRequest();
this.xhr = xhr;
return new Promise<SafeValue<string>>((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();
}
}

View File

@ -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<SafeValue<CodexDataResponse>> {
const url = this.url + Api.config.prefix + "/data";
return Fetch.safeJson<CodexDataResponse>(url, {
method: "GET",
}).then((data) => {
if (data.error) {
return data;
}
return Fetch.safeJson<CodexDataResponse>(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<CodexNodeSpace>(url, {
method: "GET",
});
return Fetch.safeJson<CodexNodeSpace>(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<SafeValue<string>>((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<SafeValue<Response>> {
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<SafeValue<NetworkDownloadResponse>> {
async networkDownload(
cid: string
): Promise<SafeValue<NetworkDownloadResponse>> {
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<SafeValue<Response>> {
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<CodexManifest>(url, {
method: "GET",
});
return Fetch.safeJson<CodexManifest>(url, { method: "GET" });
}
}

69
src/data/node-download.ts Normal file
View File

@ -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<SafeValue<string>> {
const headers: Record<string, string> = {};
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
}
}
}

View File

@ -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
}
export interface DownloadStategy {
download(url: string): Promise<SafeValue<string>>;
abort(): void;
}

1
src/node.ts Normal file
View File

@ -0,0 +1 @@
export * from "./data/node-download";